#!/usr/bin/env python3 """multi_session — Aggregate insights across ALL active sessions. Sidecar v4 looks at individual sessions. This module looks across them: - How many sessions today? - How many projects touched? - How many finished (recap_done)? - Pattern: starting many, finishing few? """ import json import os import time from pathlib import Path STATE_DIR = "/tmp" PROJECTS_DIR = Path.home() / ".claude" / "projects" def _load_all_session_states() -> list[dict]: """Load all sidecar session state files from today.""" today_start = time.mktime(time.strptime(time.strftime("%Y-%m-%d"), "%Y-%m-%d")) states = [] try: for f in Path(STATE_DIR).glob("sidecar-*.json"): if f.stat().st_mtime < today_start: continue try: with open(f, "r") as fh: state = json.load(fh) state["_file"] = f.name states.append(state) except (json.JSONDecodeError, IOError): continue except Exception: pass return states def aggregate_today() -> dict: """Build a cross-session summary for today.""" states = _load_all_session_states() if not states: return {"sessions": 0} projects = set() recapped = 0 total_calls = 0 total_errors = 0 total_edits = 0 big_sessions = 0 # >20 calls tiny_sessions = 0 # <5 calls for s in states: proj = s.get("project_name", "") if proj: projects.add(proj) if s.get("recap_done"): recapped += 1 calls = s.get("total_calls", 0) total_calls += calls if calls > 20: big_sessions += 1 elif calls < 5: tiny_sessions += 1 for h in s.get("history", []): if h.get("had_error"): total_errors += 1 if h.get("tool") in ("Edit", "Write"): total_edits += 1 return { "sessions": len(states), "projects": sorted(projects), "project_count": len(projects), "recapped": recapped, "not_recapped": len(states) - recapped, "total_calls": total_calls, "total_errors": total_errors, "total_edits": total_edits, "big_sessions": big_sessions, "tiny_sessions": tiny_sessions, "avg_calls_per_session": round(total_calls / len(states), 1) if states else 0, } def format_summary(agg: dict) -> str: """Format aggregation into a human-readable coaching message.""" if agg.get("sessions", 0) == 0: return None lines = [f"[MULTI-SESSION] Heute: {agg['sessions']} Sessions"] # Projects if agg["project_count"] > 0: lines.append(f" Projekte: {agg['project_count']} ({', '.join(agg['projects'][:5])})") # Recap rate if agg["sessions"] > 3: recap_pct = round(agg["recapped"] / agg["sessions"] * 100) if recap_pct < 30: lines.append( f" Recap: {agg['recapped']}/{agg['sessions']} ({recap_pct}%) — " "Die meisten Sessions ohne Abschluss. Wissen geht verloren." ) else: lines.append(f" Recap: {agg['recapped']}/{agg['sessions']} ({recap_pct}%)") # Tiny sessions (session sprawl indicator) if agg["tiny_sessions"] > 5: lines.append( f" Mini-Sessions (<5 Calls): {agg['tiny_sessions']} — " "Viele angefangen, wenig Substanz. Fokus-Problem?" ) # Efficiency if agg["total_calls"] > 0: error_rate = round(agg["total_errors"] / agg["total_calls"] * 100) if error_rate > 20: lines.append(f" Error-Rate: {error_rate}% — Ueberdurchschnittlich viele Fehler heute.") return "\n".join(lines)