"""Base history parser class and data structures.""" from abc import ABC, abstractmethod from dataclasses import dataclass from typing import List, Optional from datetime import datetime @dataclass class ParsedCommand: """Represents a parsed shell command.""" raw_command: str timestamp: Optional[datetime] = None line_number: int = 0 @property def normalized(self) -> str: """Return command without leading/trailing whitespace.""" return self.raw_command.strip() @property def words(self) -> List[str]: """Split command into words.""" return self.normalized.split() class HistoryParser(ABC): """Abstract base class for shell history parsers.""" SHELL_NAME: str = "base" DEFAULT_HISTORY_FILE: Optional[str] = None @abstractmethod def parse_file(self, filepath: str) -> List[ParsedCommand]: """Parse a history file and return list of commands.""" pass @abstractmethod def parse_content(self, content: str) -> List[ParsedCommand]: """Parse history content from a string.""" pass def get_default_history_locations(self) -> List[str]: """Return default locations for shell history file.""" if self.DEFAULT_HISTORY_FILE: return [self.DEFAULT_HISTORY_FILE] return [] def validate_alias_name(self, name: str) -> bool: """Validate that a name is a valid shell alias.""" if not name: return False if not name[0].isalpha() and name[0] != '_': return False for char in name: if not (char.isalnum() or char == '_'): return False return True def sanitize_alias_name(self, name: str) -> str: """Convert a string to a valid alias name.""" result = [] for i, char in enumerate(name): if char.isalnum() or char == '_': result.append(char) elif i == 0: result.append('_') sanitized = ''.join(result) if not sanitized or not (sanitized[0].isalpha() or sanitized[0] == '_'): sanitized = 'alias_' + sanitized return sanitized[:64]