Files
troupe-standup/wizard-work/spark/string_utils.py
T

135 lines
4.2 KiB
Python

#!/usr/bin/env python3
"""
String utility module: compression, formatting, and analysis helpers.
Designed for quick text manipulation tasks in the Hermes Agent workflow.
"""
import re
import base64
import zlib
from typing import Dict, List, Tuple, Optional
from collections import Counter
def compress_string(s: str) -> str:
"""Compress a string using zlib and return as base64."""
if not s:
return ""
compressed = zlib.compress(s.encode('utf-8'))
return base64.b64encode(compressed).decode('ascii')
def decompress_string(b64: str) -> str:
"""Decompress a base64-encoded zlib string."""
if not b64:
return ""
compressed = base64.b64decode(b64.encode('ascii'))
return zlib.decompress(compressed).decode('utf-8')
def count_words(s: str) -> Dict[str, int]:
"""Count word frequencies in a string."""
words = re.findall(r'\b[a-zA-Z]+\b', s.lower())
return dict(Counter(words))
def extract_emails(s: str) -> List[str]:
"""Extract all email addresses from a string."""
pattern = r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}'
return list(set(re.findall(pattern, s)))
def mask_email(email: str) -> str:
"""Mask an email address for privacy (user@x.com -> u**@*.com)."""
if '@' not in email:
return email
local, domain = email.split('@', 1)
if len(local) <= 1:
masked_local = '*'
else:
masked_local = local[0] + '*' * (len(local) - 1)
return f"{masked_local}**@*.{domain}"
def camel_to_snake(name: str) -> str:
"""Convert camelCase to snake_case."""
s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
def snake_to_camel(name: str) -> str:
"""Convert snake_case to camelCase."""
components = name.split('_')
return components[0] + ''.join(x.title() for x in components[1:])
def abbreviate(s: str, max_len: int = 50) -> str:
"""Abbreviate a string to max_len with '...' suffix if longer."""
if len(s) <= max_len:
return s
return s[:max_len - 3] + '...'
def word_wrap(text: str, width: int = 70) -> str:
"""Wrap text to specified width, preserving existing line breaks."""
paragraphs = text.split('\n\n')
wrapped = []
for p in paragraphs:
words = p.split()
lines = []
current = []
current_len = 0
for w in words:
if current_len + len(w) + (1 if current else 0) <= width:
current.append(w)
current_len += len(w) + (1 if current else 0)
else:
if current:
lines.append(' '.join(current))
current = [w]
current_len = len(w)
if current:
lines.append(' '.join(current))
wrapped.append('\n'.join(lines))
return '\n\n'.join(wrapped)
def extract_commands(text: str) -> List[str]:
"""Extract shell commands from markdown code blocks."""
pattern = r'```(?:bash|shell)?\n(.*?)\n```'
matches = re.findall(pattern, text, re.DOTALL)
commands = []
for m in matches:
for line in m.split('\n'):
line = line.strip()
if line and not line.startswith('#'):
commands.append(line)
return commands
def analyze_text(text: str) -> Dict:
"""Return text analysis: lines, words, chars, readability."""
lines = text.split('\n')
words = text.split()
chars = len(text)
avg_word_len = sum(len(w) for w in words) / len(words) if words else 0
avg_line_len = sum(len(l) for l in lines) / len(lines) if lines else 0
return {
'lines': len(lines),
'words': len(words),
'characters': chars,
'avg_word_length': round(avg_word_len, 2),
'avg_line_length': round(avg_line_len, 2),
'is_empty': chars == 0,
}
if __name__ == '__main__':
# Debug: print mask_email output
mask_test = mask_email("admin@company.co.uk")
print(f"DEBUG mask_test = {repr(mask_test)}")
print(f"DEBUG '@company.co.uk' in mask_test = {'@company.co.uk' in mask_test}")
print(f"DEBUG mask starts with 'a' = {mask_test.startswith('a')}")
print(f"DEBUG asterisks = {mask_test.count('*')}")
exit(0)