"""Tool: reconstruct current state after interruption (DB-backed).""" from __future__ import annotations import os from helpers.tool import Tool, Response from usr.plugins.a0_software_orchestrator.helpers.db_state_store import ( get_project_id, get_kv, get_status, list_next_steps, list_todos, list_errors, get_block_compact, ) from usr.plugins.a0_software_orchestrator.helpers.exceptions import PluginDBError def _resolve_name(project_name: str = "", project_root: str = "") -> str: if project_name: return project_name.strip() if project_root: return os.path.basename(project_root.rstrip("/")) return os.path.basename(os.getcwd()) class ResumeChecker(Tool): """Liest den aktuellen State aus der DB und liefert eine kompakte Recovery-Übersicht für Session-Resume nach Crash/Timeout. Args (kwargs): project_name (str): Projektname (bevorzugt). project_root (str): Legacy – wird zu basename() aufgelöst. """ async def execute( self, project_name: str = "", project_root: str = "", **kwargs, ): name = _resolve_name(project_name, project_root) try: pid = get_project_id(name) if pid is None: raise LookupError(f"project '{name}' is not registered") except (ValueError, LookupError) as e: return Response(message=f"ERROR: {e}", break_loop=False) except PluginDBError as e: return Response(message=f"Database error: {e}", break_loop=False) status = get_status(pid) next_steps = list_next_steps(pid, status="pending") open_todos = list_todos(pid, status="open") open_errors = list_errors(pid, status="open") bc = get_block_compact(pid) phase = get_kv(pid, "phase", default="") if not status and not next_steps and not bc: return Response( message=f"No state found for recovery on project {name!r}.", break_loop=False, ) lines = [f"🔁 RESUME for project {name!r} (pid={pid})"] lines.append(f" - Phase: {phase}") lines.append(f" - Status: {status or '(none)'}") lines.append(f" - Pending next steps: {len(next_steps)}") lines.append(f" - Open todos: {len(open_todos)}") lines.append(f" - Open errors: {len(open_errors)}") if bc: lines.append( f" - Last block: {bc.get('last_block_id')} " f"@ {bc.get('last_compact_at')}" ) lines.append(f" - Next block: {bc.get('next_block')}") return Response(message="\n".join(lines), break_loop=False)