From 047317fc73042a5c6f52fe3af41e764effe13f68 Mon Sep 17 00:00:00 2001 From: 7000pctAUTO Date: Sun, 1 Feb 2026 08:51:34 +0000 Subject: [PATCH] Initial upload: Shell History Alias Generator with full test suite --- shell_alias_gen/parsers/base.py | 71 +++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 shell_alias_gen/parsers/base.py diff --git a/shell_alias_gen/parsers/base.py b/shell_alias_gen/parsers/base.py new file mode 100644 index 0000000..4222ac1 --- /dev/null +++ b/shell_alias_gen/parsers/base.py @@ -0,0 +1,71 @@ +"""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]