""" Scratchpad tools for Inara. A lightweight, persistent notepad stored at inara/SCRATCH.md. Nothing here is ever distilled or archived — it is intentionally transient. Good for: working notes mid-task, half-formed ideas, things too long for a chat response but not worth saving to memory or a journal entry. Operations: scratch_read — return the full contents (or a message if empty) scratch_write — replace the entire scratchpad scratch_append — add a new timestamped section at the bottom scratch_clear — erase everything """ import asyncio from datetime import datetime, timezone from pathlib import Path from persona import persona_path def _scratch_path() -> Path: return persona_path() / "SCRATCH.md" def _now_label() -> str: return datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC") # --------------------------------------------------------------------------- # Sync implementations # --------------------------------------------------------------------------- def _scratch_read() -> str: p = _scratch_path() if not p.exists() or not p.read_text().strip(): return "Scratchpad is empty." return p.read_text() def _scratch_write(content: str) -> str: _scratch_path().write_text(content.rstrip() + "\n") return "Scratchpad updated." def _scratch_append(content: str, heading: str | None = None) -> str: p = _scratch_path() existing = p.read_text() if p.exists() else "" label = heading or _now_label() section = f"\n## {label}\n\n{content.strip()}\n" p.write_text(existing.rstrip() + "\n" + section) return f"Appended section: {label}" def _scratch_clear() -> str: p = _scratch_path() p.write_text("") return "Scratchpad cleared." # --------------------------------------------------------------------------- # Async wrappers # --------------------------------------------------------------------------- async def scratch_read() -> str: return await asyncio.to_thread(_scratch_read) async def scratch_write(content: str) -> str: return await asyncio.to_thread(_scratch_write, content) async def scratch_append(content: str, heading: str | None = None) -> str: return await asyncio.to_thread(_scratch_append, content, heading) async def scratch_clear() -> str: return await asyncio.to_thread(_scratch_clear)