From 836caf3fd21e551ffe4b31515453a9b1b75ad4b8 Mon Sep 17 00:00:00 2001 From: 7000pctAUTO Date: Sun, 1 Feb 2026 08:51:35 +0000 Subject: [PATCH] Initial upload: Shell History Alias Generator with full test suite --- shell_alias_gen/parsers/zsh.py | 52 ++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 shell_alias_gen/parsers/zsh.py diff --git a/shell_alias_gen/parsers/zsh.py b/shell_alias_gen/parsers/zsh.py new file mode 100644 index 0000000..f2e6348 --- /dev/null +++ b/shell_alias_gen/parsers/zsh.py @@ -0,0 +1,52 @@ +"""Zsh history parser.""" + +import os +import re +from datetime import datetime +from typing import List, Optional + +from .base import HistoryParser, ParsedCommand + + +class ZshHistoryParser(HistoryParser): + """Parser for Zsh history files.""" + + SHELL_NAME = "zsh" + DEFAULT_HISTORY_FILE = os.path.expanduser("~/.zsh_history") + + ZSH_HISTORY_PATTERN = re.compile( + r'^: (\d+):(\d+);(.*)$', + re.MULTILINE + ) + + def parse_file(self, filepath: str) -> List[ParsedCommand]: + """Parse a zsh 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 zsh history content from a string.""" + commands = [] + matches = self.ZSH_HISTORY_PATTERN.findall(content) + + for line_num, match in enumerate(matches, start=1): + timestamp_str, _ , command = match + try: + timestamp = datetime.fromtimestamp(int(timestamp_str)) + except (ValueError, OSError): + timestamp = None + + cmd = ParsedCommand( + raw_command=command, + timestamp=timestamp, + line_number=line_num + ) + commands.append(cmd) + + return commands