From 74eb8b87cc9726042e40f2a13644bd49b959b79d Mon Sep 17 00:00:00 2001 From: 7000pctAUTO Date: Sun, 1 Feb 2026 08:51:36 +0000 Subject: [PATCH] Initial upload: Shell History Alias Generator with full test suite --- shell_alias_gen/parsers/fish.py | 71 +++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 shell_alias_gen/parsers/fish.py diff --git a/shell_alias_gen/parsers/fish.py b/shell_alias_gen/parsers/fish.py new file mode 100644 index 0000000..d4a3210 --- /dev/null +++ b/shell_alias_gen/parsers/fish.py @@ -0,0 +1,71 @@ +"""Fish history parser.""" + +import os +import json +from datetime import datetime +from pathlib import Path +from typing import List, Optional + +from .base import HistoryParser, ParsedCommand + + +class FishHistoryParser(HistoryParser): + """Parser for Fish shell history files.""" + + SHELL_NAME = "fish" + DEFAULT_HISTORY_DIR = os.path.expanduser("~/.config/fish/history") + + def get_default_history_locations(self) -> List[str]: + """Return default locations for fish history files.""" + history_dir = Path(self.DEFAULT_HISTORY_DIR) + if history_dir.exists() and history_dir.is_dir(): + return list(history_dir.glob("*.json")) + return [] + + def parse_file(self, filepath: str) -> List[ParsedCommand]: + """Parse a fish history file.""" + try: + with open(filepath, 'r', encoding='utf-8', errors='replace') as f: + content = f.read() + return self.parse_content(content) + except FileNotFoundError: + return [] + except Exception: + return [] + + def parse_content(self, content: str) -> List[ParsedCommand]: + """Parse fish history content from a string.""" + commands = [] + + try: + data = json.loads(content) + if isinstance(data, list): + for line_num, item in enumerate(data, start=1): + if isinstance(item, dict) and 'text' in item: + timestamp = None + if 'timestamp' in item: + try: + timestamp = datetime.fromtimestamp(item['timestamp']) + except (ValueError, TypeError): + pass + + cmd = ParsedCommand( + raw_command=item['text'], + timestamp=timestamp, + line_number=line_num + ) + commands.append(cmd) + except json.JSONDecodeError: + pass + + return commands + + def parse_all_history_files(self) -> List[ParsedCommand]: + """Parse all fish history files in the default directory.""" + all_commands = [] + locations = self.get_default_history_locations() + + for filepath in locations: + all_commands.extend(self.parse_file(str(filepath))) + + return all_commands