diff --git a/README.md b/README.md index 71922fb..251181b 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ ⚡️ Delivers core agent functionality in just **~4,000** lines of code — **99% smaller** than Clawdbot's 430k+ lines. -📏 Real-time line count: **3,932 lines** (run `bash core_agent_lines.sh` to verify anytime) +📏 Real-time line count: **3,922 lines** (run `bash core_agent_lines.sh` to verify anytime) ## 📢 News diff --git a/nanobot/utils/helpers.py b/nanobot/utils/helpers.py index 8963138..8322bc8 100644 --- a/nanobot/utils/helpers.py +++ b/nanobot/utils/helpers.py @@ -1,83 +1,39 @@ """Utility functions for nanobot.""" +import re from pathlib import Path from datetime import datetime + def ensure_dir(path: Path) -> Path: - """Ensure a directory exists, creating it if necessary.""" + """Ensure directory exists, return it.""" path.mkdir(parents=True, exist_ok=True) return path def get_data_path() -> Path: - """Get the nanobot data directory (~/.nanobot).""" + """~/.nanobot data directory.""" return ensure_dir(Path.home() / ".nanobot") def get_workspace_path(workspace: str | None = None) -> Path: - """ - Get the workspace path. - - Args: - workspace: Optional workspace path. Defaults to ~/.nanobot/workspace. - - Returns: - Expanded and ensured workspace path. - """ - if workspace: - path = Path(workspace).expanduser() - else: - path = Path.home() / ".nanobot" / "workspace" + """Resolve and ensure workspace path. Defaults to ~/.nanobot/workspace.""" + path = Path(workspace).expanduser() if workspace else Path.home() / ".nanobot" / "workspace" return ensure_dir(path) -def get_sessions_path() -> Path: - """Get the sessions storage directory.""" - return ensure_dir(get_data_path() / "sessions") - - -def get_skills_path(workspace: Path | None = None) -> Path: - """Get the skills directory within the workspace.""" - ws = workspace or get_workspace_path() - return ensure_dir(ws / "skills") - - def timestamp() -> str: - """Get current timestamp in ISO format.""" + """Current ISO timestamp.""" return datetime.now().isoformat() -def truncate_string(s: str, max_len: int = 100, suffix: str = "...") -> str: - """Truncate a string to max length, adding suffix if truncated.""" - if len(s) <= max_len: - return s - return s[: max_len - len(suffix)] + suffix - +_UNSAFE_CHARS = re.compile(r'[<>:"/\\|?*]') def safe_filename(name: str) -> str: - """Convert a string to a safe filename.""" - # Replace unsafe characters - unsafe = '<>:"/\\|?*' - for char in unsafe: - name = name.replace(char, "_") - return name.strip() + """Replace unsafe path characters with underscores.""" + return _UNSAFE_CHARS.sub("_", name).strip() -def parse_session_key(key: str) -> tuple[str, str]: - """ - Parse a session key into channel and chat_id. - - Args: - key: Session key in format "channel:chat_id" - - Returns: - Tuple of (channel, chat_id) - """ - parts = key.split(":", 1) - if len(parts) != 2: - raise ValueError(f"Invalid session key: {key}") - return parts[0], parts[1] - def sync_workspace_templates(workspace: Path, silent: bool = False) -> list[str]: """Sync bundled templates to workspace. Only creates missing files.""" from importlib.resources import files as pkg_files @@ -91,7 +47,6 @@ def sync_workspace_templates(workspace: Path, silent: bool = False) -> list[str] added: list[str] = [] def _write(src, dest: Path): - """Write src content (or empty string if None) to dest if missing.""" if dest.exists(): return dest.parent.mkdir(parents=True, exist_ok=True)