Download Sidecar/multi_session.py from smlflg/Sidecar-Workspace: direct link, hf CLI and curl.
- Browser
- Download file 3.81 kB
-
https://huggingface.co/smlflg/Sidecar-Workspace/resolve/main/Sidecar/multi_session.py
- Command line
-
hf download hf://smlflg/Sidecar-Workspace/Sidecar/multi_session.py
-
curl -L -o multi_session.py https://huggingface.co/smlflg/Sidecar-Workspace/resolve/main/Sidecar/multi_session.py
3.81 kB
| #!/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) | |