#!/usr/bin/env python3
"""
Smart Session Restore — интеллектуальное восстановление сессий
Использует library.services.session.SessionManager для работы с сессиями.
Использование:
python3 smart_restore.py # Авто — найти оборванную
python3 smart_restore.py --list # Список последних сессий
python3 smart_restore.py --json # JSON для Claude
"""
import json
import argparse
import sys
import os
from pathlib import Path
from datetime import datetime, timedelta
from typing import Dict, Optional
# Добавить workspace в PYTHONPATH
WORKSPACE = Path(__file__).parent.parent.parent
sys.path.insert(0, str(WORKSPACE))
# Используем SessionManager
from library.services.session import SessionManager, Session
from library.services.session.models import TaskStatus
def classify_agent(text: str) -> str:
"""Определить агента по тексту"""
text_lower = text.lower()
if any(x in text_lower for x in ['архитектор', 'architect', 'стандарт', 'паттерн', 'теория', 'тикет']):
return 'Архитектор'
elif any(x in text_lower for x in ['проектор', 'projector', 'задач', 'план', 'фича']):
return 'Проектор'
elif any(x in text_lower for x in ['кодер', 'coder', 'код', 'функци', 'класс', 'def ', 'async', 'скрипт']):
return 'Кодер'
elif any(x in text_lower for x in ['оператор', 'operator', 'инфра', 'сервер', 'deploy', 'nginx', 'docker', 'статус', 'проверь']):
return 'Оператор'
else:
return 'Свободная'
def find_previous_session(manager: SessionManager, current_id: Optional[str] = None) -> Optional[Session]:
"""
Найти ПРЕДЫДУЩУЮ сессию (не текущую).
Логика:
1. Получаем недавние сессии
2. Пропускаем текущую
3. Возвращаем следующую (предыдущую по времени)
"""
sessions = manager.get_recent(days=7, include_closed=False)
if not sessions:
return None
# Если не передан current_id — первая = текущая
if current_id is None and sessions:
current_id = sessions[0].id
# Найти предыдущую
for session in sessions:
if session.id != current_id:
# Проверить что это реальная сессия (> 3 сообщений)
if session.msg_count >= 3:
return session
return None
def format_previous_session(session: Session, manager: SessionManager) -> str:
"""Форматировать предыдущую сессию"""
lines = []
# Анализ задач
analysis = manager.analyze_tasks(session.id)
unfinished = analysis.unfinished if analysis else []
# Текст для определения агента
all_text = ' '.join([m.get('display', '') for m in session.messages])
agent = classify_agent(all_text)
lines.append("=" * 55)
if unfinished:
lines.append("⚠️ ПРЕДЫДУЩАЯ СЕССИЯ (есть незавершённые задачи)")
else:
lines.append("📋 ПРЕДЫДУЩАЯ СЕССИЯ")
lines.append("=" * 55)
lines.append("")
# Основное
lines.append(f"Проект: {session.project.value}")
lines.append(f"Режим: {agent}")
lines.append(f"Когда: {session.last_dt.strftime('%Y-%m-%d %H:%M')}")
lines.append(f"Сообщ: {session.msg_count}")
lines.append("")
# Последний запрос
if session.summary:
lines.append(f"Тема:")
lines.append(f" \"{session.summary}\"")
lines.append("")
# TODO
if unfinished:
lines.append(f"Незавершённые задачи ({len(unfinished)}):")
for task in unfinished[:5]:
icon = "▶" if task.status == TaskStatus.IN_PROGRESS else "○"
lines.append(f" {icon} {task.name}")
lines.append("")
# Файлы
if session.files:
lines.append(f"Файлы:")
for f in session.files[-3:]:
short = f.replace('/opt/claude-workspace/', '')
lines.append(f" • {short}")
lines.append("")
lines.append("-" * 55)
lines.append("Продолжить эту сессию? [да/нет/сессии]")
lines.append("")
return '\n'.join(lines)
def format_session_list(manager: SessionManager, limit: int = 5) -> str:
"""Список сессий"""
sessions = manager.get_recent(days=3, include_closed=False)
lines = []
lines.append("=" * 55)
lines.append("ПОСЛЕДНИЕ СЕССИИ")
lines.append("=" * 55)
lines.append("")
for i, session in enumerate(sessions[:limit]):
analysis = manager.analyze_tasks(session.id)
unfinished_count = len(analysis.unfinished) if analysis else 0
todo_mark = f" ⚠{unfinished_count}" if unfinished_count else ""
all_text = ' '.join([m.get('display', '') for m in session.messages])
agent = classify_agent(all_text)
time_str = session.last_dt.strftime('%m-%d %H:%M')
lines.append(f"[{i+1}] {session.project.value:<12} {agent:<10}{todo_mark}")
lines.append(f" {time_str} | {session.msg_count} сообщ.")
if session.summary:
preview = session.summary[:40]
lines.append(f" \"{preview}\"")
lines.append("")
lines.append("-" * 55)
lines.append("'продолжить [проект]' или номер")
lines.append("")
return '\n'.join(lines)
def main():
parser = argparse.ArgumentParser(description='Smart Session Restore')
parser.add_argument('--list', action='store_true', help='Список сессий')
parser.add_argument('--json', action='store_true', help='JSON вывод')
args = parser.parse_args()
manager = SessionManager()
if args.list:
print(format_session_list(manager))
return
# Найти предыдущую сессию (не текущую)
previous = find_previous_session(manager)
if args.json:
if previous:
analysis = manager.analyze_tasks(previous.id)
unfinished = analysis.unfinished if analysis else []
result = {
'found': True,
'session_id': previous.id,
'project': previous.project.value,
'agent': classify_agent(' '.join([m.get('display', '') for m in previous.messages])),
'last_dt': previous.last_dt.isoformat(),
'msg_count': previous.msg_count,
'todos': [{'content': t.content, 'status': t.status.value} for t in unfinished],
'last_topic': previous.summary,
'files': previous.files[-5:] if previous.files else [],
}
else:
result = {'found': False}
print(json.dumps(result, indent=2, ensure_ascii=False))
return
# Текстовый вывод
if previous:
print(format_previous_session(previous, manager))
else:
print("Предыдущих сессий не найдено.\n")
print(format_session_list(manager))
if __name__ == '__main__':
main()