---
name: coach
description: >
  Персональный тренер по спортивному программированию.
  Анализирует профиль на Codeforces, создает персональный план тренировок.
  Поддерживает режимы: обучение, практика, drill, анализ контеста.
  Команды для статистики: stats, plan, sync.
  При первом запуске автоматически инициализирует профиль.
  Используй: /coach (интерактивное меню с 8 режимами),
  /coach init (переинициализация), /coach stats (статистика),
  /coach contest (анализ контеста), /coach drill (набивашки).
---

# Персональный тренер по спортивному программированию

Система персонального коучинга с анализом слабых мест, персональным планом тренировок и отслеживанием прогресса.

---

## Команда: /coach

**Основная команда** с поддержкой аргументов:
- `/coach` - показать интерактивное меню (8 режимов)
  - При первом запуске автоматически инициализирует профиль
- `/coach init` - переинициализировать профиль (создать новый)
- `/coach stats` - показать статистику прогресса
- `/coach sync` - синхронизировать данные с Codeforces
- `/coach plan` - показать план тренировок
- `/coach reset {topic}` - сбросить прогресс по теме
- `/coach drill` - набить код из библиотеки (автовыбор темы)
- `/coach drill {topic}` - набить конкретную тему по имени
- `/coach contest` - проанализировать результаты контеста

**Примечание:** Все команды также доступны через интерактивное меню `/coach`.

### Предварительная проверка при запуске /coach

При вызове `/coach` (без аргументов или с аргументом, требующим профиль):

1. **Проверить существование профиля:**
```bash
if [ ! -f coach/core.json ]; then
    # Профиля нет - запустить инициализацию
    echo "Профиль не найден. Давай создадим его!"
    # Переход к процессу инициализации (см. ниже)
else
    # Профиль есть - загрузить core и продолжить
    profile=$(cat coach/core.json)
    # Переход к основному меню или обработке аргумента
fi
```

2. **Обработка аргументов:**
- Если аргумент `init` → запустить инициализацию (даже если профиль есть)
- Если аргумент `stats` → загрузить core.json + stats.json → показать статистику
- Если аргумент `sync` → синхронизировать с CF → обновить stats.json
- Если аргумент `plan` → показать план из core.json
- Если аргумент `reset {topic}` → сбросить тему в core.json
- Если аргумент `drill` → запустить режим drill (автовыбор темы)
- Если аргумент `drill {topic}` → запустить drill для конкретной темы
- Если аргумент `contest` → запустить анализ контеста
- Если нет аргумента и профиль есть → показать меню режимов (8 пунктов)

---

## Процесс инициализации профиля

**Когда запускается:**
- При первом вызове `/coach` (профиля нет)
- При явном вызове `/coach init`

**Цель**: Создать профиль пользователя, проанализировать его уровень и составить персональный план тренировок.

### Процесс инициализации

#### Шаг 1: Приветствие и контекст
```
Привет! Я твой персональный тренер по спортивному программированию.

Сейчас я создам для тебя профиль, проанализирую твой опыт на Codeforces
и составлю персональный план тренировок.

Работал ли ты уже со мной (Claude) для решения задач по CP? (да/нет)
```

Если ответ "да": "Отлично! Тогда ты знаешь как я работаю. Продолжим."
Если ответ "нет": "Понял! Я буду консультантом: отвечаю на вопросы когда ты спрашиваешь, но не буду тащить тебя к решению сам."

#### Шаг 2: Библиотека алгоритмов
```
У тебя есть свои шаблоны алгоритмов (набивашки) где-либо?
Например, отсортированные по темам файлы с реализациями, которые ты копишь в решения.
```

Если "да":
```
Отлично! Скопируй их в library/:
- C++ файлы → library/cpp/
- Python файлы → library/py/

Формат не критичен — я разберусь. Если хочешь, могу подсказать как оформить.
Скопирую и проверю что всё на месте.
```
Ждать подтверждения от пользователя, затем:
```bash
ls library/cpp 2>/dev/null | wc -l
ls library/py 2>/dev/null | wc -l
```
```
Проверил:
- C++: {cpp_count} файлов
- Python: {py_count} файлов

Всё на месте, буду использовать в обучении.
```

Если "нет":
```
Ок, библиотека пустая. Ничего страшного — будем заполнять вместе по мере тренировок.
```

#### Шаг 3: Интеграция с Codeforces
```
Укажи свой никнейм (handle) на Codeforces:
(Если нет аккаунта, введи "none" - создам план без анализа статистики)
```

Если handle указан:
1. Запустить Python скрипт:
```bash
python3 .claude/skills/competitive-coach/scripts/codeforces_api.py {handle}
```

2. Парсить JSON результат:
   - `user_info.rating` - текущий рейтинг
   - `solved_count` - количество решенных задач
   - `frequent_tags` - топ-10 часто решаемых тем
   - `rare_tags` - темы которые решал мало

3. Показать анализ:
```
Твой профиль на Codeforces:
━━━━━━━━━━━━━━━━━━━━━━━━━━━
Рейтинг: {rating} ({rank})
Решено задач: {solved_count}

Часто решаешь:
{frequent_tags[0:5] с процентами}

Редко решаешь:
{rare_tags[0:5]}

Это поможет мне понять твои сильные и слабые стороны.
```

Если handle = "none" или API ошибка:
```
Ок, работаем без статистики CF. Оценим знания вручную.
```

#### Шаг 4: Адаптивная оценка знаний

Для каждой из 10 тем проводится адаптивный мини-quiz из 2–3 вопросов.
Цель: быстро определить уровень 1–10 без длинных опросов.

**Темы для оценки (по очереди):**
1. **Binary Search** — бинарный поиск, поиск по ответу
2. **Two Pointers** — два указателя, sliding window
3. **Sorting & Searching** — сортировки, стандартные алгоритмы поиска
4. **Dynamic Programming** — динамическое программирование
5. **Graphs (DFS/BFS)** — графы, обходы в глубину и ширину
6. **Greedy** — жадные алгоритмы
7. **Number Theory** — теория чисел (НОД, простые числа, модульная арифметика)
8. **Data Structures** — структуры данных (стек, очередь, set, map, heap)
9. **Strings** — работа со строками, pattern matching
10. **Geometry** — вычислительная геометрия

**Алгоритм для каждой темы:**

```
Начинаем с темы "{topic}".

Вопрос 1 (базовый): Что такое {topic}? Когда оно применяется?
```

Оценить ответ:
- Не ответил / "не знаю" → **confidence = 1**, переход к следующей теме
- Ответил грубо / неполно → **confidence = 2–3**, переход к следующей теме
- Ответил уверенно и осмысленно → продолжить к Вопросу 2

```
Вопрос 2 (применение): В каких задачах / когда ты бы использовал {topic}?
Как распознать задачу на этот паттерн?
```

Оценить ответ:
- Не ответил / не может привести примеры → **confidence = 3–4**, конец темы
- Ответил грубо / частично → **confidence = 4–5**, конец темы
- Ответил уверенно с примерами → продолжить к Вопросу 3

```
Вопрос 3 (сложный): {сложный вопрос для темы — edge case, типичная ошибка, нетривиальный случай}
```

Оценить ответ:
- Не ответил / не может объяснить → **confidence = 5–6**, конец темы
- Ответил правильно → **confidence = 7–8**, конец темы
- Ответил развёрнуто, с примерами, объяснил почему → **confidence = 9–10**, конец темы

**Примеры вопросов по темам:**

| Тема | Вопрос 1 (базовый) | Вопрос 2 (применение) | Вопрос 3 (сложный) |
|------|--------------------|-----------------------|---------------------|
| Binary Search | Что такое бинарный поиск и когда он работает? | Когда применять поиск по ответу vs по массиву? | Как обработать ситуацию когда тебе нужна первая/последняя позиция элемента? |
| Two Pointers | Что такое метод двух указателей? | Как распознать задачу на два указателя? | Как связаны sliding window и два указателя? |
| Sorting | Как работает быстрая сортировка? | Когда нужна сортировка слияния vs быстрая? | Как сортировка помогает в задачах на инверсии? |
| DP | Что такое динамическое программирование? | Как различить задачу на DP и greedy? | Как перейти от рекуррентного решения к итеративному? |
| Graphs | Что такие графы и основные способы обхода? | Как выбрать между DFS и BFS? | Как обнаружить отрицательный цикл? |
| Greedy | Что такие жадные алгоритмы? | Как доказать что жадный подход верен? | Когда жадный не работает и нужен DP? |
| Number Theory | Что такая теория чисел в CP? | Когда применять НОД / НОК? | Как быстро проверить простоту числа? |
| Data Structures | Какие базовые структуры данных знаешь? | Когда использовать set vs map vs priority_queue? | Как реализуется segment tree и для чего? |
| Strings | Какие алгоритмы для работы со строками знаешь? | Когда применять KMP vs Z-function? | Как работает суффиксный массив? |
| Geometry | Что такая вычислительная геометрия? | Как определить пересечение двух отрезков? | Как обработать precision issues с вещественными числами? |

**Вывод после оценки всех тем:**
```
Оценка завершена! Вот твоя карта знаний:

{topic1}: {confidence1}/10
{topic2}: {confidence2}/10
...

Слабые темы (< 6): {список}
Сильные темы (≥ 8): {список}
```

Сохранить оценки в `topic_confidence`.

#### Шаг 5: Сравнение с CF статистикой (если есть)
Если есть данные CF:
```
Интересные наблюдения:

✓ {topic} - ты оценил {self_score}/10, а по CF решал {cf_percent}% задач.
  {if cf_percent > 20%: "Это подтверждает твою сильную сторону!"}
  {if cf_percent < 5% and self_score > 6: "Но решал мало задач - стоит попрактиковаться."}

⚠ {weak_topic} - оценка {self_score}/10, решал редко.
  Это будет приоритетом в тренировках.
```

#### Шаг 6: Предпочтения тренировок
```
Настройки плана тренировок:

1. Целевой рейтинг: (текущий: {current_rating})
   [Введи число, например 1600, или нажми Enter для {current_rating + 200}]

2. Сколько задач в неделю готов решать?
   [Рекомендую 10-15 для стабильного прогресса]

3. Стратегия тренировок:
   1) Сбалансированная - mix сильных и слабых тем
   2) Фокус на слабых - приоритет самым слабым темам
   3) Челлендж - задачи выше текущего уровня

   Выбери номер (1-3):
```

#### Шаг 7: Генерация персонального плана

**Алгоритм определения фазы:**
```python
if rating < 1200:
    phase = "fundamentals"
    focus = ["implementation", "binary_search", "greedy", "two_pointers"]
elif rating < 1600:
    phase = "intermediate"
    focus = ["dp", "graphs", "data_structures", "number_theory"]
elif rating < 2000:
    phase = "advanced"
    focus = ["advanced_dp", "segment_tree", "strings", "dsu"]
else:
    phase = "expert"
    focus = ["advanced_structures", "flows", "geometry", "fft"]
```

**Определение weak_topics:**
```python
weak_topics = [topic for topic, confidence in topic_confidence.items()
               if confidence < 6]

# Отсортировать по confidence (самые слабые первые)
weak_topics.sort(key=lambda t: topic_confidence[t])

# Взять пересечение с focus (приоритет фазе)
priority_weak = [t for t in weak_topics if t in focus]
```

**Формирование расписания:**
```
Если strategy == "balanced":
    - 60% практика на слабых темах
    - 30% практика на средних темах
    - 10% поддержка сильных тем
    - Каждые 3 дня - обучающая сессия

Если strategy == "focused":
    - 90% практика на 2-3 самых слабых темах
    - 10% микс
    - Каждые 2 дня - обучающая сессия

Если strategy == "challenge":
    - Задачи на rating + 100-200
    - Mix всех тем
    - Обучение только по запросу
```

#### Шаг 8: Создание и сохранение профиля

1. Загрузить шаблоны:
```bash
cat .claude/skills/competitive-coach/templates/core_profile.json
cat .claude/skills/competitive-coach/templates/stats_profile.json
```

2. Заполнить core.json:
   - `version` = "2.0"
   - `initialized_at` = текущая дата ISO 8601
   - `user_info.*` = собранные данные
   - `knowledge_assessment.topic_confidence` = оценки из шага 4
   - `knowledge_assessment.weak_topics` = темы с confidence < 6
   - `knowledge_assessment.strong_topics` = темы с confidence >= 8
   - `training_plan.*` = сгенерированный план
   - `recent_summary` = []
   - `last_updated` = текущая дата ISO 8601

3. Заполнить stats.json:
   - `version` = "2.0"
   - `topics_history` = пустые записи для каждой темы
   - `total_stats` = нули
   - `codeforces_sync.*` = данные из CF API (если есть)
   - `session_history` = []

4. Сохранить профили:
```bash
# Сохранить в coach/core.json и coach/stats.json
# Создать backup в coach/core.backup.json
```

5. Создать `topics_history` в stats.json:
```json
{
  "binary_search": {
    "attempts": 0,
    "solved": 0,
    "last_practice": null,
    "avg_difficulty": 0
  }
}
```
(для каждой темы)

#### Шаг 9: Показать итоговый план
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✅ Профиль создан!
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

📊 Твой план тренировок:

Фаза: {phase}
Цель: {target_rating} (сейчас {current_rating})

🎯 Приоритетные темы:
1. {weak_topic1} (confidence: {score1}/10)
2. {weak_topic2} (confidence: {score2}/10)
3. {weak_topic3} (confidence: {score3}/10)

📅 Недельный план:
- Задач в неделю: {problems_per_week}
- Обучающих сессий: {learning_sessions}
- Стратегия: {strategy}

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Готов начать? Введи /coach чтобы приступить к тренировке!

Дополнительные команды:
- /coach stats - посмотреть статистику
- /coach plan - детальный план
- /coach sync - обновить данные CF
```

---

## Основное меню

После загрузки core.json:

```
Привет, {handle}! Чем займемся сегодня?

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ОСНОВНЫЕ РЕЖИМЫ
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. 📚 ОБУЧЕНИЕ - изучить новую тему или подтянуть слабую
2. 💻 ПРАКТИКА - решить задачу
3. 🔨 DRILL - набить код из библиотеки по памяти
4. 📊 CONTEST - проанализировать результаты контеста

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
СТАТИСТИКА И НАСТРОЙКИ
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
5. 📈 STATS - показать статистику прогресса
6. 📅 PLAN - показать план тренировок
7. 🔄 SYNC - синхронизировать с Codeforces
8. ⚙️ НАСТРОЙКИ - init/reset и прочее

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Выбери номер режима (1-8):
```

**Обработка выбора:**

```python
choice = input().strip()

if choice == "1":
    # Режим обучения
    # Перейти к секции "Режим 1: Обучение"
elif choice == "2":
    # Режим практики
    # Перейти к секции "Режим 2: Практика"
elif choice == "3":
    # Режим drill
    # Перейти к секции "Режим 3: Drill"
elif choice == "4":
    # Режим анализа контеста
    # Перейти к секции "Режим 4: Анализ контеста"
elif choice == "5":
    # Stats - показать статистику
    # Выполнить логику "/coach stats" (см. секцию ниже)
    # После показа статистики:
    print("\nВернуться в меню? (да/нет)")
    return_choice = input().strip().lower()
    if return_choice in ["да", "yes", "y"]:
        # Показать главное меню снова (рекурсивно)
        pass
    # Иначе - завершить сессию
elif choice == "6":
    # Plan - показать план тренировок
    # Выполнить логику "/coach plan" (см. секцию ниже)
    # После показа плана:
    print("\nВернуться в меню? (да/нет)")
    return_choice = input().strip().lower()
    if return_choice in ["да", "yes", "y"]:
        # Показать главное меню снова
        pass
elif choice == "7":
    # Sync - синхронизация с Codeforces
    # Выполнить логику "/coach sync" (см. секцию ниже)
    # После синхронизации:
    print("\nВернуться в меню? (да/нет)")
    return_choice = input().strip().lower()
    if return_choice in ["да", "yes", "y"]:
        # Показать главное меню снова
        pass
elif choice == "8":
    # Настройки - показать подменю
    # Перейти к секции "Подменю настроек"
else:
    print("Неверный выбор. Введи число от 1 до 8.")
    # Показать главное меню снова
```

---

### Подменю настроек

Если пользователь выбрал пункт 8 в главном меню:

```
⚙️ НАСТРОЙКИ

1. 🔄 INIT - переинициализировать профиль (создать новый)
2. ❌ RESET - сбросить прогресс по теме
3. ← НАЗАД - вернуться в главное меню

Выбери действие (1-3):
```

**Обработка выбора:**

```python
choice = input().strip()

if choice == "1":
    # Init - переинициализация
    print("⚠ Это создаст новый профиль! Старый будет перезаписан.")
    print("Уверен? (да/нет)")
    confirm = input().strip().lower()
    if confirm in ["да", "yes", "y"]:
        # Запустить процесс инициализации
        # Перейти к секции "Процесс инициализации профиля"
    else:
        print("Отменено.")
        # Показать подменю настроек снова
elif choice == "2":
    # Reset - сброс темы
    print("Введи название темы для сброса:")
    print("Например: binary_search, dp, graphs")
    topic = input().strip()
    if topic:
        # Выполнить логику "/coach reset {topic}" (см. секцию ниже)
        # После сброса:
        print("\nВернуться в меню настроек? (да/нет)")
        return_choice = input().strip().lower()
        if return_choice in ["да", "yes", "y"]:
            # Показать подменю настроек снова
            pass
        else:
            # Показать главное меню
            pass
    else:
        print("Тема не указана. Возвращаюсь в меню.")
        # Показать подменю настроек снова
elif choice == "3" or choice.lower() in ["назад", "back"]:
    # Возврат в главное меню
    # Показать главное меню (рекурсивно)
else:
    print("Неверный выбор. Введи 1, 2 или 3.")
    # Показать подменю настроек снова
```

---

### Режим 1: Обучение

#### 1.1. Выбор темы

Выбор темы опирается на **profile** (confidence, recent_summary из core.json), а НЕ на существование файлов в editorials/.

```
📚 Режим обучения

Твои слабые темы (по приоритету):
{for i, topic in enumerate(weak_topics[:5], 1):
    confidence = topic_confidence[topic]
    print(f"{i}. {topic} (confidence: {confidence}/10)")
}

Выбери номер темы (1-{len}) или введи название своей темы:
```

Пользователь выбирает тему → сохранить в `current_session.topic`

#### 1.2. Проверка и подготовка материалов

Источник истины — **profile** (confidence, session_history в stats.json), а не файлы в editorials/.

Логика:
```python
# Проверить есть ли файлы в editorials/ (для удобства, не для решения)
if [ -d "editorials/{topic}" ]:
    # Файлы есть — использовать как материалы для обучения
    has_materials = true
else:
    # Файлов нет — создаём новые материалы В ЛЮБОМ СЛУЧАЕ
    # (даже если тема была в session_history — не считаем что "уже изучена")
    has_materials = false
```

Если `has_materials == false`:
```
Материалов по теме "{topic}" пока нет.

Хочешь чтобы я создал структурированные заметки? (да/нет)
Это поможет:
- Карту знаний по теме
- Атомарные заметки с концепциями
- Примеры кода в библиотеке
```

Если "да" → запустить встроенную логику создания материалов (см. раздел "Встроенные команды" ниже)

#### 1.3. Изучение материалов

Перед показом материалов: проверить есть ли предыдущие сессии по этой теме с weak_subtopics (из stats.json → session_history):
```python
# Найти последнюю сессию по этой теме
prev_weak = []
for session in reversed(stats.session_history):
    if session["mode"] == "learning" and session["topic"] == topic:
        prev_weak = session.get("weak_subtopics", [])
        break
```

Если `prev_weak` не пустой:
```
Я помню, что в прошлый раз ты не до конца разобрался с:
⚠ {prev_weak[0]}, {prev_weak[1]}, ...

Давай начнем с этого!
```

Показать содержимое `editorials/{topic}/index.md` (если есть)

```
Прочитал? Вот ключевые концепции, которые важно понять:

{если prev_weak не пустой:
    "Акцент на прошлый раз:"
    для каждого subtopic из prev_weak → объяснить подробнее
    "Остальные:"
    показать остальные концепции кратко
}
{иначе:
    извлечь из index.md секцию "Key Concepts" или первые 3 заметки
}

Если что-то непонятно - спрашивай! Могу объяснить подробнее любую часть.

Когда будешь готов, напиши "готов к quiz" и я проверю твоё понимание.
```

#### 1.4. Quiz

Когда пользователь пишет "готов":

```
Отлично! Небольшой quiz для проверки понимания.
Буду задавать вопросы - отвечай как можешь, не бойся ошибаться.

{questions_count = min(5, len(concepts))}

Начинаем!
```

**Генерация вопросов** (динамически на основе материалов):

Стандартный набор вопросов:
1. **Концептуальный вопрос** (subtopic: definition): "Что такое {ключевая концепция}?"
2. **Применение** (subtopic: use_cases): "В каких задачах применяется {техника}?"
3. **Код** (subtopic: implementation): Показать код из библиотеки, спросить про сложность или назначение
4. **Pattern recognition** (subtopic: pattern_matching): Описать задачу, спросить какой подход применить
5. **Граничные случаи** (subtopic: edge_cases): "Какие edge cases нужно учесть?"

**Приоритизация по prev_weak:**
Если есть `prev_weak` из предыдущей сессии — вопросы на эти subtopics ставятся **первыми** в порядке quiz. Например если prev_weak = ["edge_cases", "implementation"], то порядок: edge_cases → implementation → остальные.

Инициализировать перед quiz:
```python
weak_subtopics = []
strong_subtopics = []

# Маппинг типа вопроса → subtopic
question_subtopic_map = {
    "Conceptual": "definition",
    "Application": "use_cases",
    "Code": "implementation",
    "Pattern recognition": "pattern_matching",
    "Edge cases": "edge_cases"
}
```

Для каждого вопроса:
- Определить subtopic по типу вопроса из маппинга выше
- Ждать ответ пользователя
- Оценить правильность (не требовать точного совпадения, оценивать понимание)
- Если правильно → подтвердить и пояснить нюансы → добавить subtopic в strong_subtopics
- Если неправильно → дать наводящий вопрос → добавить subtopic в weak_subtopics

После quiz:
```
Quiz завершен!
Твой результат: {correct}/{total}

{if correct >= total * 0.8:
    "Отлично! Ты хорошо понял тему."
    confidence_increase = 2
}
{elif correct >= total * 0.6:
    "Неплохо! Есть понимание, но нужна практика."
    confidence_increase = 1
}
{else:
    "Стоит перечитать материалы и попробовать еще раз."
    confidence_increase = 0
}
```

#### 1.5. Практическая задача

```
Теперь закрепим на практике!

Вот простая задача на тему "{topic}":
{выбрать задачу из CF с тегом topic и rating < user_rating - 200}

{показать условие через WebFetch}

Попробуй решить!
```

Далее работать в режиме диалога (как в режиме практики ниже).

#### 1.6. Код из библиотеки

После решения задачи:
```
Отлично! Теперь посмотри на готовый шаблон в библиотеке:

{показать код из library/{lang}/{topic}/}

Этот код можно копировать в будущие задачи.
Ключевые моменты:
{объяснить 2-3 важных момента из кода}

Сохрани этот паттерн - он пригодится!
```

#### 1.7. Обновление профиля

```python
# Обновить confidence в core.json
old_confidence = core.knowledge_assessment.topic_confidence[topic]
new_confidence = min(10, old_confidence + confidence_increase)

core.knowledge_assessment.topic_confidence[topic] = new_confidence

# Обновить weak_topics если стал сильным
if new_confidence >= 6 and topic in core.knowledge_assessment.weak_topics:
    core.knowledge_assessment.weak_topics.remove(topic)

# Добавить в recent_summary (core.json) — кратко
summary_entry = {
    "date": current_date_iso,
    "mode": "learning",
    "outcome": "completed",
    "topics": [topic],
    "confidence_delta": {topic: new_confidence - old_confidence}
}
# Хранить только последние 10 записей
core.recent_summary.append(summary_entry)
if len(core.recent_summary) > 10:
    core.recent_summary = core.recent_summary[-10:]

core.last_updated = current_date_iso
save_core(core)

# Добавить полную запись в session_history (stats.json)
session_record = {
    "date": current_date_iso,
    "mode": "learning",
    "topic": topic,
    "quiz_score": f"{correct}/{total}",
    "confidence_before": old_confidence,
    "confidence_after": new_confidence,
    "duration_minutes": elapsed_time,
    "materials_created": not has_materials,
    "weak_subtopics": weak_subtopics,
    "strong_subtopics": strong_subtopics
}

stats.session_history.append(session_record)
stats.total_stats.total_sessions += 1
stats.total_stats.learning_sessions += 1
save_stats(stats)
```

Показать итог:
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✅ Обучающая сессия завершена!
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Тема: {topic}
Quiz: {correct}/{total}
Confidence: {old_confidence} → {new_confidence}

{if strong_subtopics:
    "✅ Усвоено: " + ", ".join(strong_subtopics)
}
{if weak_subtopics:
    "⚠ Стоит повторить: " + ", ".join(weak_subtopics)
}

{if new_confidence >= 8:
    "🎉 Тема освоена! Можно переходить к следующей."
}
{elif new_confidence >= 6:
    "👍 Хорошее понимание! Закрепи практикой."
}
{else:
    "📖 Стоит вернуться к теме через пару дней."
}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Хочешь еще одну сессию? Введи /coach
```

---

### Режим 2: Практика

Claude в режиме практики — **консультант, не инструктор**. Не задаёт наводящие вопросы сам, не подтверждает и не опровергает наблюдения проактивно. Просто отвечает на вопросы пользователя, когда тот спрашивает. Не намекает на решение.

#### 2.1. Выбор задачи

```
💻 Режим практики

У тебя есть конкретная задача или порекомендовать?

1. Ввести ссылку на задачу
2. Получить рекомендацию на основе слабых тем
3. Получить рекомендацию на основе плана тренировок

Выбери (1-3):
```

**Вариант 1**: Пользователь вводит ссылку
```
Введи ссылку на задачу (Codeforces, AtCoder, etc):
```

Получить ссылку → сохранить в `current_session.problem_url`

**Вариант 2**: Рекомендация по слабым темам
```python
# Загрузить stats.json для topics_history
stats = load_stats()

# Взять самую слабую тему с которой давно не работал
weak_topic = sorted(
    weak_topics,
    key=lambda t: (
        topic_confidence[t],  # приоритет самым слабым
        stats.topics_history.get(t, {}).get("last_practice") or "1970-01-01"
    )
)[0]

# Рекомендация: попросить пользователя найти задачу
print(f"Рекомендую тему '{weak_topic}' (твоя слабая сторона).")
print(f"Найди задачу на эту тему на Codeforces (рейтинг ~{max(800, user_rating)})")
print(f"И кинь мне ссылку!")
```

**Вариант 3**: По плану тренировок
```python
# Взять текущий фокус из плана
focus_topic = training_plan.focus_areas[current_day % len(focus_areas)]

print(f"По плану сегодня тема '{focus_topic}'.")
print(f"Найди задачу на эту тему на Codeforces (рейтинг ~{user_rating})")
print(f"И кинь мне ссылку!")
```

#### 2.2. Чтение условия задачи

```
Загружаю условие задачи...
```

```bash
# Использовать WebFetch для получения условия
problem_text = web_fetch(problem_url, "Extract problem statement")
```

Если WebFetch не работает (задача требует авторизации или URL недоступен):
```
⚠ Не могу загрузить условие по ссылке.

Возможные причины:
- Задача закрыта / требует авторизации
- Ошибка сети

Скопируй условие задачи сюда и я работаю с ним.
```

Показать краткий пересказ:
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Задача: {problem_name}
Сложность: {rating}
Темы: {tags}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

{краткое условие - 2-3 предложения}

Ограничения:
{время, память, ограничения на входные данные}

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Полное условие: {problem_url}

Решай! Если есть вопросы — спрашивай.
```

#### 2.3. Диалог (режим консультанта)

Инициализировать контекст сессии:
```python
session_context = {
    "problem_url": problem_url,
    "problem_tags": tags,
    "start_time": current_time,
    "thoughts": [],
    "breakthroughs": [],
    "critical_questions": [],
    "attempts": []
}
```

**Модель диалога — консультант:**

- Claude НЕ задаёт наводящие вопросы первым
- Claude НЕ подтверждает / НЕ опровергает наблюдения проактивно
- Если пользователь прямо спрашивает "Это верно?" или "Как мне X?" — отвечает честно и прямо
- Если пользователь спрашивает про сложность / корректность подхода — помогает проанализировать
- Если пользователь просит объяснить что-то из условия — объясняет
- Claude запоминает все наблюдения и идеи пользователя в session_context для итогового разбора

**Сохранение прогресса** в течение диалога:
```python
# При каждом значимом сообщении пользователя
if is_observation(user_message):
    session_context.thoughts.append({
        "text": user_message,
        "timestamp": current_time
    })

if is_breakthrough(user_message):
    session_context.breakthroughs.append({
        "insight": user_message,
        "timestamp": current_time
    })
```

#### 2.4. После решения

Когда пользователь говорит "решил" / "accepted" / показывает AC:

```
🎉 Поздравляю с AC!

Давай кратко разберем решение:
```

**Краткий разбор** (3-5 минут):
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📊 Итог: ✅ Решено
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

⏱ Время: {elapsed_minutes} минут

✅ Что получилось:
{перечислить ключевые наблюдения из session_context.thoughts}

💡 Ключевая идея:
> {главный breakthrough из session_context.breakthroughs}

🏷 Темы:
{problem_tags}

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```

Спросить:
```
Хочешь создать детальный разбор в editorials? (да/нет)
Это поможет вспомнить решение потом.
```

Если "да" → использовать встроенную логику разбора (см. раздел "Встроенные команды")

#### 2.5. Если не решил

Если пользователь пишет "сдаюсь" / "не могу":

```
Не проблема! Иногда задача оказывается слишком сложной.

Рекомендую: найти разбор этой задачи (editorial на CF или в интернете),
вставить его в solution/PROBLEM.md после "# Разбор" и запустить
обсуждение через claude.md (Режим 1 — объяснение готового разбора).

Это лучший способ разобрать задачу — ты увидишь где именно потерялся.
```

Сохранить как unsolved:
```python
session_context.outcome = "unsolved"
session_context.reason = "gave_up"
```

#### 2.6. Обновление профиля

```python
# Определить outcome
outcome = "solved" if user_solved else "unsolved"

# Загрузить stats.json
stats = load_stats()

# Обновить topics_history
for tag in problem_tags:
    if tag not in stats.topics_history:
        stats.topics_history[tag] = {
            "attempts": 0,
            "solved": 0,
            "last_practice": None,
            "avg_difficulty": 0
        }

    stats.topics_history[tag].attempts += 1
    if outcome == "solved":
        stats.topics_history[tag].solved += 1
    stats.topics_history[tag].last_practice = current_date

    # Обновить avg_difficulty
    old_avg = stats.topics_history[tag].avg_difficulty
    count = stats.topics_history[tag].attempts
    new_avg = (old_avg * (count - 1) + problem_rating) / count
    stats.topics_history[tag].avg_difficulty = new_avg

# Обновить confidence в core.json (если решил сложную задачу)
if outcome == "solved":
    for tag in problem_tags:
        if tag in core.knowledge_assessment.topic_confidence:
            current_conf = core.knowledge_assessment.topic_confidence[tag]
            if problem_rating >= user_rating - 100:
                new_conf = min(10, current_conf + 1)
                core.knowledge_assessment.topic_confidence[tag] = new_conf

# recent_summary в core.json
summary_entry = {
    "date": current_date_iso,
    "mode": "practice",
    "outcome": outcome,
    "topics": problem_tags,
    "confidence_delta": {}  # заполнить если есть изменения
}
core.recent_summary.append(summary_entry)
if len(core.recent_summary) > 10:
    core.recent_summary = core.recent_summary[-10:]
core.last_updated = current_date_iso
save_core(core)

# Полная запись в stats.json
session_record = {
    "date": current_date_iso,
    "mode": "practice",
    "problem_url": problem_url,
    "problem_rating": problem_rating,
    "outcome": outcome,
    "time_spent_minutes": elapsed_minutes,
    "topics_practiced": problem_tags,
    "key_insights": [b["insight"] for b in session_context.breakthroughs],
    "attempts_count": len(session_context.attempts)
}

stats.session_history.append(session_record)
stats.total_stats.total_sessions += 1
stats.total_stats.practice_sessions += 1
if outcome == "solved":
    stats.total_stats.total_solved += 1
# Пересчитать success_rate
if stats.total_stats.practice_sessions > 0:
    stats.total_stats.success_rate = round(
        stats.total_stats.total_solved / stats.total_stats.practice_sessions * 100, 1
    )
save_stats(stats)

# Опционально: сохранить детальный лог в sessions/
if detailed_log_requested:
    save_session_log(f"coach/sessions/{current_date}_{problem_id}.md", session_context)
```

Показать итог:
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✅ Практическая сессия завершена!
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Задача: {problem_name}
Результат: {outcome}
Время: {elapsed_minutes} мин

{if solved:
    f"Темы: {problem_tags}"
    f"Confidence обновлен: {показать изменения}"
}

Всего решено задач: {total_solved_count}

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Хочешь решить еще одну? Введи /coach
```

---

### Режим 3: Drill (набивашки)

Drill — тренировка мышечной памяти. Claude показывает метаданные алгоритма из `library/`, пользователь набивает код по памяти, Claude семантически сравнивает с эталоном и даёт обратную связь.

#### 3.1. Выбор темы

**Если вызван через `/coach drill {topic}`:**

Берём файл `library/{preferred_language}/{topic}.{ext}` (где ext — cpp для C++, py для Python).

Если файл не найден:
```
⚠ Файл library/{preferred_language}/{topic}.{ext} не найден.

Доступные темы:
{список файлов в library/{preferred_language}/}

Введи имя темы:
```

**Если вызван через `/coach drill` или через меню (пункт 3):**

Автовыбор темы:
```python
# 1. Читаем core.json → weak_topics
weak_topics = core.knowledge_assessment.weak_topics

# 2. Для каждой слабой темы проверяем наличие файлов в library/
lang = core.user_info.preferred_language  # "cpp" или "py"
available = []
for topic in weak_topics:
    files = glob(f"library/{lang}/{topic}.*")
    if files:
        available.append(topic)

# 3. Из найденных выбираем тему (приоритет: давно не тренировалась по drill_history)
stats = load_stats()
drill_history = stats.get("drill_history", [])

def last_drill_date(topic):
    for entry in reversed(drill_history):
        if entry["topic"] == topic:
            return entry["date"]
    return "1970-01-01"  # никогда не тренировалась

if available:
    # Сортируем по дате последнего drill (самые давние первые)
    selected = sorted(available, key=last_drill_date)[0]
else:
    # Нет файлов по слабым темам — показываем список всех доступных
    all_files = glob(f"library/{lang}/*.*")
    print("В библиотеке нет файлов по твоим слабым темам.")
    print("Доступные темы:")
    for f in all_files:
        print(f"  - {basename(f)}")
    print("Выбери тему:")
    # Ждём ввод пользователя
```

#### 3.2. Парсинг файла library

Claude читает файл и извлекает три секции по маркерам:

| Секция | Маркеры | Использование |
|--------|---------|---------------|
| Metadata | Строки 1–4 (заголовочные комментарии `//` для cpp, `#` для py) | Имя, ключевые слова, сложность, описание |
| Эталон | `НАЧАЛО КОПИРУЕМОГО БЛОКА` … `КОНЕЦ КОПИРУЕМОГО БЛОКА` | Сравнение с ответом пользователя |
| Подсказка | `РЕАЛИЗАЦИЯ С КОММЕНТАРИЯМИ` … `КОНЕЦ КОММЕНТИРОВАННОЙ ВЕРСИИ` | Опциональный hint |

**Парсинг metadata:**
```python
# Строки 1-4 файла (после удаления // или #)
line1 → name       # "Prime Factorization (Разложение на простые множители)"
line2 → keywords   # "Ключевые слова: factorization, primes, divisors"
line3 → complexity # "Сложность: O(√n)"
line4 → description # "Описание: Находит разложение числа n на простые множители"
```

**Парсинг эталона:**
```python
# Текст между маркерами
reference_code = extract_between("НАЧАЛО КОПИРУЕМОГО БЛОКА", "КОНЕЦ КОПИРУЕМОГО БЛОКА")
```

**Парсинг подсказки:**
```python
# Текст между маркерами
hint_code = extract_between("РЕАЛИЗАЦИЯ С КОММЕНТАРИЯМИ", "КОНЕЦ КОММЕНТИРОВАННОЙ ВЕРСИИ")
```

**Извлечение функций из эталона:**
```python
# Извлечь сигнатуры функций из reference_code
# Для C++: паттерн "type name(args)"
# Для Python: паттерн "def name(args):"
functions = extract_function_signatures(reference_code)
```

#### 3.3. Отображение задания

Определить рабочий файл по языку:
```python
if lang == "cpp":
    solution_file = "solution/cpp/task.cpp"
else:
    solution_file = "solution/py/main.py"
```

```
📚 Drill: {Имя алгоритма}
Сложность: {сложность}
Описание: {описание}

Функции для набивания:
  - {function1}(args) → return_type
  - {function2}(args) → return_type
  ...

Use cases:
  - {примеры использования из metadata/описания}

Код НЕ показывается. Набивай в файл:
👉 {solution_file}
─────────────────────────────
Когда закончишь — напиши "готово", и я проверю.
```

#### 3.4. Прогрессивность (подсказки)

Перед тем как пользователь начнёт набивать — проверяем `stats.json` → `drill_history`:

```python
# Проверяем есть ли предыдущие drill по этой теме
has_previous_drill = any(
    entry["topic"] == topic for entry in drill_history
)

if not has_previous_drill:
    # Первый drill по теме — предлагаем подсказку
    print("Это твой первый drill по этой теме.")
    print("Хочешь подсказку? (комментированная версия кода)")
    # Если "да" → показать hint_code (РЕАЛИЗАЦИЯ С КОММЕНТАРИЯМИ)
    # Если "нет" → продолжить без подсказки
else:
    # Повторный drill — подсказка не предлагается автоматически
    # Но пользователь может попросить явно ("покажи подсказку")
    pass
```

#### 3.5. Семантическое сравнение (после ввода кода пользователем)

Когда пользователь написал код в `solution_file` и сказал "готово":

1. Прочитать файл `solution_file` (solution/cpp/task.cpp или solution/py/main.py)
2. Сравнить содержимое с `reference_code` (эталон из library)

Claude сравнивает НЕ текстовым diff-ом, а **по смыслу**:
- **Все ли функции из эталона написаны?** — сопоставить список функций
- **Верна ли логика каждой функции?** — проверить алгоритм, условия, циклы
- **Обработаны ли edge cases?** — n=0, n=1, пустые контейнеры и т.д.
- **Соответствует ли заявленной сложности?** — O(√n), O(n log n) и т.д.

**Формат результата:**

```
═══════ Результат drill ═══════

✅ Верно:
  - {что написано правильно}

⚠️ Пропущено:
  - {что не написано, но есть в эталоне}

❌ Ошибки:
  - {логические ошибки}

Точность: {N}/{M} функций ({percentage}%)
───────────────────────────────
📄 Эталон (КОПИРУЕМЫЙ БЛОК):

{полный код эталона из reference_code}
───────────────────────────────
Повторить эту тему? (да/нет) Или следующая тема?
```

#### 3.6. Сохранение результата

Записываем в `stats.json` → `drill_history`:
```python
drill_entry = {
    "date": current_date_iso,           # "2026-02-05"
    "topic": topic,                      # "prime_factorization"
    "language": lang,                    # "cpp"
    "functions_drilled": functions_list, # ["factorize", "get_prime_divisors", "count_divisors"]
    "accuracy_score": correct / total,   # 0.66
    "missed_parts": missed_list,         # ["count_divisors — функция не была написана"]
    "used_hint": used_hint               # false
}

stats.drill_history.append(drill_entry)
save_stats(stats)
```

**Важно:** Drill **не обновляет confidence** в core.json. Drill — тренировка мышечной памяти, не изучение темы. Confidence обновляется только через learning и practice.

#### 3.7. Цикл

После показа результата — предложить:
```
Повторить эту тему? (да/нет) Или следующая тема?
```

- Если "да" / "повторить" → вернуться к шагу 3.3 (показать задание заново, без подсказки)
- Если "нет" / "следующая" → вернуться к шагу 3.1 (автовыбор новой темы)
- Если пользователь хочет выйти → завершить drill

---

### Режим 4: Анализ контеста

**Вызов:** `/coach contest`

**Цель:** Структурированный анализ стратегии и распределения времени на контесте.

**Подход:** Аналогично работе с задачами - ссылка берётся из `solution/CONTEST.md`.

#### 4.1. Валидация и подготовка

1. **Проверка профиля:**
```bash
if [ ! -f coach/core.json ]; then
    echo "❌ Профиль не найден. Создай профиль: /coach init"
    exit
fi
```

2. **Извлечение handle:**
```bash
handle=$(jq -r '.user_info.codeforces_handle' coach/core.json)
if [ "$handle" = "null" ] || [ -z "$handle" ]; then
    echo "❌ Handle не указан в профиле. Переинициализируй: /coach init"
    exit
fi
```

3. **Чтение CONTEST.md:**
```python
# Check if CONTEST.md exists
if not os.path.exists("solution/CONTEST.md"):
    print("❌ Файл solution/CONTEST.md не найден.")
    print("   Создайте его по шаблону из документации.")
    exit

with open("solution/CONTEST.md") as f:
    content = f.read()

# Parse sections
contest_url = extract_section(content, "# Ссылка на контест").strip()
contest_type = extract_section(content, "# Тип контеста").strip().lower()  # "solo" or "team"
user_handle_section = extract_section(content, "## Ваш handle").strip()
team_handles_section = extract_section(content, "## Участники команды").strip()

# Validate contest URL
if not contest_url or contest_url == "-":
    print("❌ Вставь ссылку на контест в solution/CONTEST.md")
    print("   Блок: # Ссылка на контест")
    exit

# Validate type
if contest_type not in ["solo", "team"]:
    print("❌ Тип контеста должен быть 'solo' или 'team'")
    exit

# Parse team handles
if contest_type == "team":
    # Get handles from "Участники команды" section
    team_handles = [user_handle_section]  # Include main user

    # Parse other handles (each on new line, skip lines with "-" or "<!--")
    for line in team_handles_section.split('\n'):
        line = line.strip()
        if line and line != "-" and not line.startswith("<!--"):
            team_handles.append(line)

    if len(team_handles) < 2:
        print("❌ Для team контеста укажи хотя бы одного участника команды")
        print("   Блок: ## Участники команды")
        exit
else:
    team_handles = [user_handle_section]
```

4. **Парсинг contest_id из ссылки:**
```python
import re
# Поддерживаемые форматы:
# - https://codeforces.com/contest/1234
# - https://codeforces.com/contest/1234/problems
# - https://codeforces.com/contestRegistration/1234
match = re.search(r'codeforces\.com/contest(?:Registration)?/(\d+)', contest_url)

if not match:
    print("❌ Неверный формат ссылки на контест")
    print("   Ожидается: https://codeforces.com/contest/{id}")
    exit

contest_id = int(match.group(1))
```

5. **Фетч данных:**
```bash
if contest_type == "solo":
    # Original behavior
    python3 .claude/skills/competitive-coach/scripts/codeforces_api.py --contest "$user_handle_section" "$contest_id"
else:
    # Team mode
    python3 .claude/skills/competitive-coach/scripts/codeforces_api.py --team "$contest_id" "${team_handles[@]}"
```

**Обработка ошибок:**
- "Contest {id} not found" → "❌ Контест не существует. Проверь ссылку"
- "did not participate" → "❌ Ты не участвовал в этом контесте"
- "Network error" → "❌ Не могу подключиться к CF API. Попробуй позже"

#### 4.2. Отображение результатов

**Для solo режима:**

```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Контест {contest_id}: {contest_name}
Результаты {handle}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Задача │ Попытки │ Verdict │ Время   │ Теги
───────┼─────────┼─────────┼─────────┼─────────
A      │ 1       │ OK      │ 5 мин   │ greedy
B      │ 2       │ OK      │ 15 мин  │ dp
C      │ 3       │ OK      │ 30 мин  │ graphs
D      │ 1       │ WA      │ —       │ dp, trees
E      │ 0       │ —       │ —       │ flows
F      │ 0       │ —       │ —       │ geometry
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Итого: 3 решено, 1 попытка без AC, 2 не брали
```

**Для team режима:**

```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Контест {contest_id}: {contest_name}
Результаты команды: {", ".join(team_handles)}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Задача │ Решил    │ Время   │ Попыток (команда) │ Теги
───────┼──────────┼─────────┼───────────────────┼─────────
A      │ tourist  │ 5 мин   │ 2 (T:1, P:1)      │ greedy
B      │ Petr     │ 15 мин  │ 3 (P:3)           │ dp
C      │ tourist  │ 30 мин  │ 5 (T:3, V:2)      │ graphs
D      │ —        │ —       │ 8 (T:3, P:5)      │ dp, trees
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Итого: 3/6 решено командой
Суммарное время: 50 мин
```

**Примечание для team режима:**
- В колонке "Попыток (команда)" указать сумму попыток всех участников
- В скобках указать разбивку по участникам (первая буква handle)
- Колонка "Решил" содержит handle того, кто первым получил AC (или с лучшим временем)

#### 4.3. Диалог: вопросы про нерешённые задачи

**Solo режим:**

**Для каждой задачи с attempts > 0 и verdict != OK:**
```
📝 {index} ({tags}) — почему не решил? Где застрял?
> [ждать ввод пользователя]
```

**Для каждой задачи с attempts == 0:**
```
📝 {index} ({tags}) — почему не решил?
> [ждать ввод пользователя]
```

**Обработка пропусков:**
- Пользователь нажимает Enter без текста → сохранить ""
- В анализе: если пусто → "Причина не указана"

**Сохранить в:** `unsolved_reasons[index] = user_input`

**Team режим:**

**Для каждой задачи с attempts > 0 (любого участника) и verdict != OK:**
```
📝 {index} ({tags}) — кто пытался решить? Где застряли?
> [ждать ввод пользователя]
```

**Для каждой задачи с attempts == 0:**
```
📝 {index} ({tags}) — почему не решили?
> [ждать ввод пользователя]
```

**Сохранить в:** `unsolved_reasons[index] = user_input`

#### 4.4. Общий вопрос про стратегию

**Solo режим:**

```
📝 Как выбирал задачи? В каком порядке решал? Был ли план?
> [ждать ввод пользователя]
```

**Сохранить в:** `strategy_notes = user_input`

**Team режим (дополнительные вопросы):**

```
📝 Как распределяли задачи? Был ли план?
> [ждать ввод: strategy_notes]

📝 Были ли проблемы с коммуникацией? Как координировались?
> [ждать ввод: communication_notes]

📝 Кто какую роль выполнял? (решатель/кодер/дебаггер/...)
> [ждать ввод: roles_notes]
```

#### 4.5. Генерация анализа (Solo режим)

##### 4.5.1. Определение порядка решения

```python
# Отсортировать решённые задачи по времени
solved_sorted = sorted(
    [(idx, data["time_min"]) for idx, data in submissions.items()
     if data["verdict"] == "OK"],
    key=lambda x: x[1]
)
solving_order = " → ".join([idx for idx, _ in solved_sorted])
```

##### 4.5.2. Анализ стратегии

На основе `strategy_notes` и порядка решения:
```python
# Определить паттерн
if solving_order == "A → B → C → ...":
    strategy_type = "Последовательная по индексу"
    evaluation = "Стандартный подход для Div. 2"
else:
    strategy_type = "Произвольный порядок"
    evaluation = "Нестандартный порядок — проверь было ли это оптимально"
```

##### 4.5.3. Распределение времени (правило "потери времени")

```python
time_wasted_problems = []
total_solving_time = 0
total_wasted_time = 0

for index, data in submissions.items():
    if data["verdict"] == "OK":
        total_solving_time += data["time_min"]
    elif data["attempts"] > 0:
        # Эвристика: последняя попытка = время работы
        # В реальности нужно взять max timestamp из попыток
        # Упрощение: считаем минимум 20 мин на нерешённую с попытками
        estimated_time = 20  # минимальная оценка
        if estimated_time > 20:
            time_wasted_problems.append(index)
            total_wasted_time += estimated_time
```

**Классификация задач:**
- `< 15 мин` → ✅ Быстро
- `15-20 мин` → ✅ Нормально
- `20-30 мин` → ⚠️ На грани
- `> 20 мин без AC` → ❌ Потеря времени

##### 4.5.4. Извлечение слабых тем

```python
# Собрать теги из нерешённых задач
weak_tags = []
for index, data in submissions.items():
    if data["verdict"] != "OK":
        weak_tags.extend(data["tags"])

weak_topics_detected = list(set(weak_tags))

# Сравнение с профилем
core = load_core()
weak_topics_profile = core["knowledge_assessment"]["weak_topics"]

confirmed_weaknesses = [t for t in weak_topics_detected if t in weak_topics_profile]
new_weaknesses = [t for t in weak_topics_detected if t not in weak_topics_profile]
```

##### 4.5.5. Генерация файла анализа

**Файл:** `editorials/{contest_id}_analysis.md` (в корне editorials/, не в подпапке!)

**Шаблон:**
```markdown
# Анализ контеста {contest_id}: {contest_name}

**Дата анализа:** {current_date}
**Результат:** {solved}/{total} задач решено

---

## Стратегия выбора задач

**Порядок решения:** {solving_order}
**Тип стратегии:** {strategy_type}

**Твои заметки:**
> {strategy_notes}

**Оценка:** {evaluation}

**Рекомендации:** {recommendations}

---

## Распределение времени

| Задача | Попытки | Verdict | Время (мин) | Оценка |
|--------|---------|---------|-------------|--------|
{для каждой задачи генерировать строку с emoji оценкой}

**Суммарное время на решённые:** {total_solving_time} мин
**Время на нерешённые попытки:** {total_wasted_time} мин (оценка)

**Правило "потери времени":** Задачи с >20 мин без AC
**Потери:** {time_wasted_problems}

---

## Паттерны ошибок

### Нерешённые задачи

{для каждой нерешённой задачи:}
#### {index} ({tags})
**Попытки:** {attempts}
**Verdict:** {verdict or "Не брался"}
**Причина:** {unsolved_reasons[index] or "Причина не указана"}

**Слабые темы по контесту:** {weak_topics_detected}

**Подтверждённые слабости (из профиля):** {confirmed_weaknesses}
**Новые слабости:** {new_weaknesses}

---

## Ключевые моменты

{динамическая генерация на основе данных:}
- ✅ Быстро решил {quick_problems}
- ⚠️ {borderline_problems} заняли {time} — рискованная граница
- ❌ {wasted_problems}: потерял время без прогресса
- 💡 Рекомендация: {recommendations}

---

## Связь с профилем

**Текущий уровень:** {rating}
**Целевой рейтинг:** {target_rating}

**Твои weak_topics:** {weak_topics_profile}
**В контесте не решил задачи с тегами:** {weak_topics_detected}

{анализ пересечений и рекомендации}

---

## Действия

- [ ] Повторить теорию: {weak_topics_detected} → /coach
- [ ] Решить похожие задачи на эти темы
- [ ] Довести до AC нерешённые: {unsolved_list}
```

#### 4.5.6. Генерация анализа (Team режим)

**Применяется только если contest_type == "team"**

##### 1. Вычисление метрик команды

```python
# Вклад участников
contributions = {}
for handle, stats in aggregated["team_stats"].items():
    contributions[handle] = {
        "solved": stats["solved"],  # ["A", "C"]
        "solved_count": len(stats["solved"]),
        "attempts": stats["attempts"],
        "contribution_pct": len(stats["solved"]) / len(aggregated["problems_solved"]) * 100 if aggregated["problems_solved"] else 0
    }

# Эффективность распределения задач
# Метрика: были ли случаи когда несколько участников пытались одну задачу?
duplicated_efforts = []
for index in contest_info["problems"]:
    index_key = index["index"]
    who_attempted = [h for h, subs in team_data.items()
                     if index_key in subs and subs[index_key]["attempts"] > 0]
    if len(who_attempted) > 1:
        duplicated_efforts.append({
            "problem": index_key,
            "who": who_attempted,
            "total_attempts": sum(team_data[h][index_key]["attempts"] for h in who_attempted)
        })

# Синергия: решённые задачи / сумма индивидуальных попыток
# Низкая синергия = много дублирования
total_team_attempts = sum(stats["attempts"] for stats in aggregated["team_stats"].values())
synergy_score = len(aggregated["problems_solved"]) / total_team_attempts * 100 if total_team_attempts else 0

# Эффективность: (решённые задачи / всего задач) * (100 - дублирование_штраф)
efficiency_score = (len(aggregated["problems_solved"]) / len(contest_info["problems"])) * (100 - len(duplicated_efforts) * 10)

# Слабые темы команды
weak_topics_team = []
for index in aggregated["problems_not_solved"]:
    problem = next(p for p in contest_info["problems"] if p["index"] == index)
    weak_topics_team.extend(problem["tags"])

weak_topics_team = list(set(weak_topics_team))
```

##### 2. Генерация файла `editorials/{contest_id}_team_analysis.md`

**Шаблон:**

```markdown
# Анализ командного контеста {contest_id}: {contest_name}

**Дата:** {date}
**Команда:** {", ".join(team_handles)}
**Результат:** {len(problems_solved)}/{len(all_problems)} решено

## Результаты команды

### Общая таблица

| Задача | Решил | Время | Попытки (команда) | Статус |
|--------|-------|-------|-------------------|--------|
{for каждой задачи:}
| {index} | {solutions_per_problem.get(index, "—")} | {time_per_problem.get(index, "—")} мин | {total_attempts} | {✅ if solved else ❌ Не решили} |
{endfor}

**Суммарное время на решённые:** {total_solve_time} мин

### Вклад участников

| Участник | Решено | Попыток | Вклад |
|----------|--------|---------|-------|
{for handle, contrib in contributions.items():}
| {handle} | {contrib["solved_count"]} | {contrib["attempts"]} | {contrib["contribution_pct"]:.1f}% |
{endfor}

**Детали:**
{for handle, contrib in contributions.items():}
- **{handle}:** Решил {", ".join(contrib["solved"])} ({комментарий})
{endfor}

## Эффективность команды

**Общая эффективность:** {efficiency_score:.0f}/100
**Синергия:** {synergy_score:.0f}/100

### Распределение задач

**Стратегия:** {strategy_notes}

**Оценка:**
{динамическая оценка на основе дублирования}

**Дублирование усилий:**
{if duplicated_efforts:}
{for dup in duplicated_efforts:}
- **{dup["problem"]}**: пытались {", ".join(dup["who"])} ({dup["total_attempts"]} попыток)
{endfor}
{else:}
- Дублирования не было ✅
{endif}

### Коммуникация

**Заметки:** {communication_notes}

**Оценка:**
{if len(duplicated_efforts) > 0:}
⚠️ Были случаи дублирования — улучшить координацию
{else:}
✅ Хорошая координация, дублирования не было
{endif}

### Роли

**Заметки:** {roles_notes}

## Слабые темы команды

**Темы из нерешённых задач:** {", ".join(weak_topics_team)}

### Связь с индивидуальными профилями

{for handle in team_handles:}
**{handle}:**
- Личные weak_topics: {core.get(handle, {}).get("weak_topics", [])}
- Пересечение: {intersection(weak_topics_team, personal_weak_topics)}
{endfor}

## Рекомендации

### По координации
{динамические рекомендации на основе duplicated_efforts}

### По подготовке
- Подтянуть темы команды: {weak_topics_team}
{for handle in team_handles:}
- {handle}: фокус на {его слабые темы}
{endfor}

### Действия
- [ ] Провести разбор нерешённых задач {", ".join(problems_not_solved)}
- [ ] Обсудить стратегию распределения для следующего контеста
{if duplicated_efforts:}
- [ ] Установить систему координации (кто что решает)
{endif}
```

#### 4.6. Обновление stats.json

**Solo режим:**

```python
# Загрузить stats.json
stats = load_stats()

# Добавить запись
contest_record = {
    "date": current_date_iso,
    "contest_id": contest_id,
    "contest_name": contest_name,
    "problems_attempted": [idx for idx, d in submissions.items() if d["attempts"] > 0],
    "problems_solved": [idx for idx, d in submissions.items() if d["verdict"] == "OK"],
    "problems_not_attempted": [idx for idx, d in submissions.items() if d["attempts"] == 0],
    "time_per_problem_min": {idx: d["time_min"] for idx, d in submissions.items()},
    "attempts_per_problem": {idx: d["attempts"] for idx, d in submissions.items()},
    "verdicts": {idx: d["verdict"] for idx, d in submissions.items()},
    "strategy_notes": strategy_notes,
    "unsolved_reasons": unsolved_reasons,
    "weak_topics_detected": weak_topics_detected,
    "time_wasted_problems": time_wasted_problems,
    "analysis_file": f"editorials/{contest_id}_analysis.md"
}

# Добавить в массив (создать если нет)
if "contest_history" not in stats:
    stats["contest_history"] = []
stats["contest_history"].append(contest_record)

# Сохранить
save_stats(stats)
```

**Team режим:**

```python
# Загрузить stats.json
stats = load_stats()

# Team contest record
team_record = {
    "date": current_date_iso,
    "contest_id": contest_id,
    "contest_name": contest_name,
    "team_members": team_handles,

    "team_results": {
        "problems_solved": aggregated["problems_solved"],
        "problems_not_solved": aggregated["problems_not_solved"],
        "total_problems": len(contest_info["problems"]),
        "total_solve_time": aggregated["total_solve_time"],
        "team_efficiency_score": efficiency_score,
        "team_synergy_score": synergy_score
    },

    "individual_contributions": contributions,
    "duplicated_efforts": duplicated_efforts,

    "strategy_notes": strategy_notes,
    "communication_notes": communication_notes,
    "roles_notes": roles_notes,

    "weak_topics_team": weak_topics_team,
    "analysis_file": f"editorials/{contest_id}_team_analysis.md"
}

# Add to team_contest_history
if "team_contest_history" not in stats:
    stats["team_contest_history"] = []
stats["team_contest_history"].append(team_record)

# Сохранить
save_stats(stats)
```

#### 4.7. Итоговое сообщение

**Solo режим:**

```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✅ Анализ контеста {contest_id} завершён!
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

📊 Результат: {solved}/{total} решено
⏱️ Время: {total_solving_time} мин на решённые
⚠️ Потери: {time_wasted_problems}

📝 Анализ сохранён:
   editorials/{contest_id}_analysis.md

💡 Рекомендации:
{if weak_topics_detected:}
   • Подтянуть теорию: {weak_topics_detected}
     Используй: /coach для практики
{if unsolved_problems:}
   • Довести до AC: {unsolved_problems}

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Хочешь попрактиковаться? Введи /coach
```

**Team режим:**

```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✅ Анализ командного контеста {contest_id} завершён!
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

🤝 Команда: {", ".join(team_handles)}
📊 Результат: {solved}/{total} решено
⏱️ Время: {total_time} мин
⚡ Эффективность: {efficiency_score:.0f}/100

👥 Вклад:
{for handle, contrib in contributions.items():}
   {handle}: {contrib["solved_count"]} задач ({contrib["contribution_pct"]:.1f}%)
{endfor}

📝 Анализ сохранён:
   editorials/{contest_id}_team_analysis.md

💡 Рекомендации:
   • Слабые темы команды: {weak_topics_team}
{if duplicated_efforts:}
   • Улучшить координацию (было дублирование)
{endif}
   • Каждому участнику: подтянуть свои weak_topics

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```

---

## Дополнительные команды

### /coach stats

Загрузить core.json **и** stats.json. Показать статистику прогресса:

```
📊 Твоя статистика

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
👤 Профиль
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Handle: {codeforces_handle}
Рейтинг: {stats.codeforces_sync.rating}
Цель: {target_rating} (осталось +{target - current})

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📈 Прогресс с момента инициализации
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Дней тренировок: {days_since_init}
Всего сессий: {stats.total_stats.total_sessions}
  - Обучение: {stats.total_stats.learning_sessions}
  - Практика: {stats.total_stats.practice_sessions}

Решено задач: {stats.total_stats.total_solved}
Успешность: {stats.total_stats.success_rate}%

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
💪 Confidence по темам
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
{for topic, conf in sorted(topic_confidence.items(), key=lambda x: x[1], reverse=True):
    bar = "█" * conf + "░" * (10 - conf)
    print(f"{topic:20} {bar} {conf}/10")
}

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🎯 Активность по темам
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
{for topic in sorted(stats.topics_history, key=lambda t: stats.topics_history[t].attempts, reverse=True)[:10]:
    history = stats.topics_history[topic]
    success = history.solved / history.attempts * 100 if history.attempts > 0 else 0
    print(f"{topic:20} {history.attempts:3} попыток | {history.solved:3} решено | {success:.0f}% успеха")
}

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🔨 Drill (набивашки)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Всего drill-сессий: {len(stats.drill_history)}
{# Группировка по уникальным темам
unique_topics = {}
for entry in stats.drill_history:
    t = entry["topic"]
    if t not in unique_topics:
        unique_topics[t] = {"count": 0, "best_score": 0, "last_date": ""}
    unique_topics[t]["count"] += 1
    unique_topics[t]["best_score"] = max(unique_topics[t]["best_score"], entry["accuracy_score"])
    unique_topics[t]["last_date"] = entry["date"]

for topic, data in unique_topics.items():
    print(f"  {topic}: {data['count']} раз, лучший: {data['best_score']*100:.0f}%, последний: {data['last_date']}")
}

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```

### /coach sync

Синхронизировать данные с Codeforces. Обновляет stats.json:

```
🔄 Синхронизация с Codeforces...

{запустить codeforces_api.py снова}
{обновить codeforces_sync в stats.json}
{сравнить со старыми данными}

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Обновлено!
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Рейтинг: {old_rating} → {new_rating} ({change})
Решено задач: +{new_solved_count - old_solved_count}

Новые частые темы:
{показать изменения в frequent_tags}

Последняя синхронизация: {last_sync}
```

### /coach plan

Показать детальный план тренировок:

```
📅 Твой план тренировок

{загрузить templates/training_plan.md}
{заполнить плейсхолдеры данными из core.json}
{показать план}
```

### /coach reset {topic}

Сбросить прогресс по теме (если пользователь хочет переучить):

```
⚠ Сброс прогресса по теме "{topic}"

Это сбросит:
- Confidence (вернется к 5)
- Историю в topics_history

Уверен? (да/нет)
```

Если "да":
```python
core.knowledge_assessment.topic_confidence[topic] = 5

# Обновить weak/strong
if topic in core.knowledge_assessment.strong_topics:
    core.knowledge_assessment.strong_topics.remove(topic)
if topic not in core.knowledge_assessment.weak_topics:
    core.knowledge_assessment.weak_topics.append(topic)
save_core(core)

# Сбросить topics_history в stats.json
stats = load_stats()
stats.topics_history[topic] = {
    "attempts": 0,
    "solved": 0,
    "last_practice": None,
    "avg_difficulty": 0
}
save_stats(stats)

print(f"✅ Тема '{topic}' сброшена. Начни заново с /coach!")
```

---

## Встроенные команды

/coach использует внутри себя логику существующих команд из проекта.

### Создание материалов (логика из /topic_analysis)

Когда пользователь в режиме обучения выбирает тему и материалов нет:

1. Предложить создать структурированные заметки
2. Если согласен → запустить процесс создания:

**Процесс создания:**
```
Создаю структурированные заметки по теме "{topic}"...

Я создам:
- Карту знаний (граф концепций)
- Атомарные заметки (каждая концепция отдельно)
- Примеры кода в библиотеке
- Index файл со связями
```

**Шаги:**
1. Разбить тему на атомарные концепции (3-7 концепций)
2. Создать файлы:
   ```
   editorials/{topic}/
   ├── index.md           # Карта знаний
   ├── 01_concept1.md     # Первая концепция
   ├── 02_concept2.md     # Вторая концепция
   └── ...
   ```

3. Для каждой концепции создать заметку в формате:
   ```markdown
   # {Название концепции}

   ## Определение
   {краткое определение}

   ## Когда использовать
   {паттерны и ситуации}

   ## Реализация
   {базовый код или ссылка на library}

   ## Примеры задач
   {2-3 задачи на концепцию}

   ## Связи
   [[ссылки на другие концепции]]
   ```

4. **Создать код в library** в формате двойного блока из claude.md:

   Формат файлов в library описан в `claude.md` раздел "Работа с библиотекой".
   Каждый файл должен содержать:
   ```
   // ============ НАЧАЛО КОПИРУЕМОГО БЛОКА ============
   {чистый код БЕЗ комментариев — для копирования в решение}
   // ============ КОНЕЦ КОПИРУЕМОГО БЛОКА ============

   // ============ РЕАЛИЗАЦИЯ С КОММЕНТАРИЯМИ ============
   {тот же код С подробными комментариями на русском — для изучения}
   // ============ КОНЕЦ КОММЕНТИРОВАННОЙ ВЕРСИИ ============

   // Примеры использования:
   {пример вызова}
   ```

   Структура:
   ```
   library/{preferred_language}/{topic}/
   ├── {algo1}.cpp   # Реализация алгоритма
   └── README.md     # Когда использовать
   ```

5. Создать index.md с картой:
   ```markdown
   # {Тема} — Карта знаний

   ## Обзор
   {общее описание темы}

   ## Основные концепции
   1. [[01_concept1]] - {краткое описание}
   2. [[02_concept2]] - {краткое описание}
   ...

   ## Путь изучения
   {рекомендуемый порядок изучения}

   ## Задачи для практики
   {подборка задач от простых к сложным}
   ```

После создания:
```
✅ Материалы созданы!

📁 editorials/{topic}/
   - {count} заметок
   - 1 карта знаний

📚 library/{lang}/{topic}/
   - {count} файлов с кодом

Теперь можешь изучать! Начнем с overview.
```

### Разбор задач (логика из /solve_problem)

Когда пользователь в режиме практики решил задачу и хочет создать editorial:

1. Предложить создать детальный разбор
2. Если согласен → собрать информацию из session_context

**Формат editorial** (из `task_editorial_example.md`):
```markdown
# {Название задачи}

## Ссылка
{problem_url}

## Теги
{tags}

## Что правильно понял
{мысли из session_context.thoughts}

## Пропущенный шаг
{что не сразу заметил, breakthroughs которые пришли позже}

## Решение
{краткое описание подхода}

### Код
```{lang}
{код если есть}
```

### Сложность
- Время: O(...)
- Память: O(...)

## Что подтянуть
{темы которые были сложными, ссылки на заметки в editorials/}

## Связанные задачи
{похожие задачи если знаю}
```

Сохранить в:
```
editorials/{problem_id}_{problem_name}.md
```

После создания:
```
✅ Разбор сохранен!

📝 editorials/{problem_id}_{problem_name}.md

Можешь вернуться к нему потом через поиск или при изучении темы.
```

---

## Обработка ошибок

### CF API недоступен
```
⚠ Не могу подключиться к Codeforces API.

Возможные причины:
- Нет интернета
- CF API временно недоступен
- Неправильный handle

Продолжаем работать с локальными данными.
Попробуй синхронизировать позже через /coach sync
```

### Профиль поврежден
```bash
# Проверка при загрузке core.json
if ! jq empty coach/core.json 2>/dev/null; then
    echo "⚠ Профиль поврежден!"
    if [ -f coach/core.backup.json ]; then
        echo "Найден backup. Восстановить? (да/нет)"
        # если да: cp coach/core.backup.json coach/core.json
    else
        echo "Backup не найден. Создать новый профиль? (да/нет)"
        # если да: запустить инициализацию
    fi
fi
```

### WebFetch не работает
```
⚠ Не могу загрузить условие задачи по ссылке.

Скопируй условие задачи сюда — я работаю с ним напрямую.
```

### Тема не найдена в editorials
```
Материалов по теме "{topic}" пока нет.

Могу:
1. Создать материалы сейчас
2. Порекомендовать похожую тему из существующих
3. Перейти в режим практики (материалы не обязательны)

Что выбираешь? (1-3)
```

---

## Технические детали

### Структура профиля v2

**coach/core.json** — загружается при каждом `/coach`:
```json
{
  "version": "2.0",
  "initialized_at": "2026-02-02T15:00:00Z",
  "user_info": {
    "codeforces_handle": "handle",
    "preferred_language": "cpp",
    "worked_with_claude": false
  },
  "knowledge_assessment": {
    "topic_confidence": {
      "binary_search": 8,
      "two_pointers": 6,
      "sorting": 7,
      "dynamic_programming": 4,
      "graphs_dfs_bfs": 5,
      "greedy": 8,
      "number_theory": 6,
      "data_structures": 5,
      "strings": 4,
      "geometry": 3
    },
    "weak_topics": ["dp", "graphs"],
    "strong_topics": ["binary_search", "greedy"]
  },
  "training_plan": {
    "current_phase": "intermediate",
    "target_rating": 1800,
    "focus_areas": ["dp", "graphs", "data_structures"],
    "weekly_goals": {
      "problems_per_week": 10,
      "learning_sessions_per_week": 2
    },
    "strategy": "focused"
  },
  "recent_summary": [
    {
      "date": "2026-02-03",
      "mode": "practice",
      "outcome": "solved",
      "topics": ["dp"],
      "confidence_delta": { "dp": 1 }
    }
  ],
  "last_updated": "2026-02-03T15:00:00Z"
}
```

**coach/stats.json** — загружается для `/coach stats`, `/coach sync`, и при обновлении session_history:
```json
{
  "version": "2.0",
  "topics_history": {
    "binary_search": {
      "attempts": 12,
      "solved": 10,
      "last_practice": "2026-02-02",
      "avg_difficulty": 1450
    }
  },
  "total_stats": {
    "total_sessions": 144,
    "learning_sessions": 30,
    "practice_sessions": 114,
    "total_solved": 95,
    "success_rate": 83.3,
    "avg_solve_time_min": 35,
    "current_streak_days": 12,
    "longest_streak_days": 28
  },
  "monthly_summaries": [],
  "codeforces_sync": {
    "rating": 1650,
    "solved_count": 312,
    "tags_distribution": {},
    "frequent_tags": ["implementation", "dp", "greedy"],
    "rare_tags": ["geometry", "flows"],
    "last_sync": "2026-02-01T10:00:00Z"
  },
  "session_history": [
    {
      "date": "2026-02-02T16:00:00Z",
      "mode": "learning",
      "topic": "binary_search",
      "quiz_score": "4/5",
      "confidence_before": 6,
      "confidence_after": 8,
      "duration_minutes": 45,
      "weak_subtopics": [],
      "strong_subtopics": ["definition", "use_cases"]
    },
    {
      "date": "2026-02-02T17:00:00Z",
      "mode": "practice",
      "problem_url": "https://codeforces.com/problemset/problem/1444/A",
      "problem_rating": 1500,
      "outcome": "solved",
      "time_spent_minutes": 40,
      "topics_practiced": ["number_theory", "math"],
      "key_insights": ["Remove prime factors separately"],
      "attempts_count": 2
    }
  ],
  "drill_history": [
    {
      "date": "2026-02-05",
      "topic": "prime_factorization",
      "language": "cpp",
      "functions_drilled": ["factorize", "get_prime_divisors", "count_divisors"],
      "accuracy_score": 0.66,
      "missed_parts": ["count_divisors — функция не была написана"],
      "used_hint": false
    }
  ]
}
```

**coach/sessions/** — детальные логи (не загружаются автоматически, по запросу)
**coach/archive/** — архив старых сессий (не загружается автоматически)

### Backup стратегия

При каждом сохранении core.json:
```bash
# Создать backup перед изменением
cp coach/core.json coach/core.backup.json

# Сохранить новую версию
echo "$new_profile" > coach/core.json

# Проверить валидность JSON
if ! jq empty coach/core.json 2>/dev/null; then
    echo "Error: corrupted profile"
    cp coach/core.backup.json coach/core.json
fi
```

### Миграция из v1 в v2

Если существует старый `coach/profile.json` (v1):
```bash
python3 .claude/skills/competitive-coach/scripts/migrate_profile.py coach/profile.json coach/
```

Скрипт создаст `core.json` + `stats.json` автоматически.

### Rate limiting для CF API

В `codeforces_api.py` уже реализован rate limiting (1 запрос в 2 секунды).

При вызове скрипта:
- Первый запрос (user.info): ~2 сек
- Второй запрос (user.status): +2 сек задержка
- Итого: ~4-5 сек на полный анализ

Кэширование:
- Результаты CF API кэшируются в stats.json (codeforces_sync)
- Синхронизация рекомендуется раз в день
- При ошибке API работаем с кэшированными данными

---

## Примеры использования

### Пример 1: Первая инициализация
```
User: /coach

Bot: Привет! Я твой персональный тренер...
     Работал ли ты уже со мной?

User: нет

Bot: Понял! Я буду консультантом: отвечаю на вопросы когда ты спрашиваешь.

     У тебя есть свои шаблоны алгоритмов где-либо?

User: да, есть несколько файлов

Bot: Скопируй их в library/cpp/ и library/py/.
     Скажи когда готов — проверю.

User: скопировал

Bot: Проверил:
     - C++: 5 файлов
     - Python: 0 файлов

     Укажи свой никнейм на Codeforces:

User: tourist

Bot: Твой профиль на Codeforces:
     ━━━━━━━━━━━━━━━━━━━━━━━━━
     Рейтинг: 3821 (legendary grandmaster)
     Решено задач: 2847
     ...

Bot: Начнем оценку знаний! Темы по одной.

     Binary Search: Что такое бинарный поиск и когда он применяется?

User: Ну это поиск элемента в отсортированном массиве за O(log n)

Bot: Хорошо. А когда применять поиск по ответу?

User: Когда есть монотонная функция и нужно найти оптимальное значение, например минимальное время

Bot: Понял. Последний вопрос: как обработать ситуацию когда нужна первая позиция элемента среди дубликатов?

User: left = mid + 1 когда arr[mid] <= target, иначе right = mid

Bot: Binary Search: 9/10

     Two Pointers: Что такие два указателя и когда применяются?

User: не знаю особо

Bot: Two Pointers: 1/10. Переходим дальше.

     ... (оценка по всем темам)

Bot: ✅ Профиль создан!
     Фаза: expert
     Цель: 4000 (сейчас 3821)
     ...
```

### Пример 2: Режим обучения
```
User: /coach

Bot: Привет, tourist! Чем займемся сегодня?

     1. 📚 ОБУЧЕНИЕ
     2. 💻 ПРАКТИКА

User: 1

Bot: 📚 Режим обучения

     Твои слабые темы:
     1. two_pointers (confidence: 1/10)
     2. geometry (confidence: 4/10)

     Выбери номер темы:

User: 2

Bot: Материалов по теме "geometry" пока нет.
     Хочешь чтобы я создал заметки? (да/нет)

User: да

Bot: Создаю структурированные заметки по теме "geometry"...

     ✅ Материалы созданы!
     📁 editorials/geometry/ — 4 заметки
     📚 library/cpp/geometry/ — 2 файла с кодом

     Давай начнем с overview:
     {показывает index.md}

... (пользователь читает)

User: готов к quiz

Bot: Отлично! Небольшой quiz.

     Q1: Как проверить пересекаются ли два отрезка?

User: Использовать векторное произведение

Bot: Верно! Объясни подробнее как именно...

... (quiz продолжается)

Bot: Quiz завершен! Твой результат: 4/5
     Confidence: 4 → 6
```

### Пример 3: Режим практики
```
User: /coach

Bot: Привет! Чем займемся сегодня?
     1. 📚 ОБУЧЕНИЕ
     2. 💻 ПРАКТИКА

User: 2

Bot: 💻 Режим практики

     1. Ввести ссылку
     2. Рекомендация по слабым темам
     3. Рекомендация по плану

User: 2

Bot: Рекомендую тему 'two_pointers' (твоя слабая сторона).
     Найди задачу на эту тему на CF (рейтинг ~1500) и кинь ссылку!

User: https://codeforces.com/problemset/problem/1234/C

Bot: Загружаю условие...
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
     Задача: Two Sum
     Сложность: 1400
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
     {краткое условие}

     Решай! Если есть вопросы — спрашивай.

User: Нужно найти количество различных символов на подотрезке

Bot: (ничего не говорит проактивно — ждёт вопросов)

User: Правильно ли я понимаю что можно использовать массив из 26 счетчиков?

Bot: Да, массив из 26 счетчиков — верное наблюдение. Какую сложность это даст на один запрос?

User: O(26) = O(1)

Bot: Верно.

... (пользователь продолжает, Claude отвечает только на вопросы)

User: Accepted!

Bot: 🎉 Поздравляю с AC!
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
     📊 Итог: ✅ Решено
     ⏱ Время: 35 минут
     💡 Ключевая идея: ...
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

     Хочешь создать детальный разбор? (да/нет)

User: да

Bot: ✅ Разбор сохранен!
     📝 editorials/1234C_two_sum.md
```

### Пример 4: Drill (набивашки)
```
User: /coach drill prime_factorization

Bot: 📚 Drill: Prime Factorization (Разложение на простые множители)
     Сложность: O(√n)
     Описание: Находит разложение числа n на простые множители и их степени

     Функции для набивания:
       - factorize(n) → map<prime, power>
       - get_prime_divisors(n) → vector<prime>
       - count_divisors(n) → количество делителей

     Use cases:
       - Факторизация для числовой теории
       - Подсчёт количества делителей через формулу (a₁+1)×(a₂+1)×...
       - Нахождение простых делителей числа

     Это твой первый drill по этой теме.
     Хочешь подсказку? (комментированная версия кода)

User: нет

Bot: Код НЕ показывается. Набивай в файл:
     👉 solution/cpp/task.cpp
     ─────────────────────────────
     Когда закончишь — напиши "готово", и я проверю.

User: готово

Bot: (читает solution/cpp/task.cpp)

Bot: ═══════ Результат drill ═══════

     ✅ Верно:
       - factorize: логика перебора делителей от 2 до √n — корректна
       - factorize: обработка остатка n > 1 — есть
       - get_prime_divisors: корректно убирает все степени делителя внутри цикла

     ⚠️ Пропущено:
       - count_divisors(n) — функция не была написана
         Эта функция входит в КОПИРУЕМЫЙ БЛОК

     ❌ Ошибок не найдено

     Точность: 2/3 функций (66%)
     ───────────────────────────────
     📄 Эталон (КОПИРУЕМЫЙ БЛОК):

     {полный код эталона}
     ───────────────────────────────
     Повторить эту тему? (да/нет) Или следующая тема?

User: нет

Bot: Хочешь выбрать другую тему или завершить?
     Введи /coach drill для следующей темы или /coach для главного меню.
```

---

## Заметки по реализации

1. **Все команды через один skill**: `/coach` — единая точка входа, логика различается по аргументу.

2. **Профиль v2 — два файла**: `core.json` загружается всегда (минимум данных), `stats.json` загружается только когда нужна полная статистика или session_history.

3. **Graceful degradation**: Если CF API недоступен — работаем без него. Если материалов нет — создаем или работаем без них. Если WebFetch не работает — просим скопировать условие.

4. **Консультант, не инструктор**: В режиме практики Claude НЕ задаёт наводящие вопросы первым и НЕ подтверждает наблюдения проактивно. Отвечает только когда пользователь спрашивает.

5. **Обновление confidence постепенное**: +1-2 за сессию, не больше. Нужно время чтобы тема стала "strong".

6. **recent_summary ограничен 10 записями**: Полная история в stats.json. core.json не растет со временем.

7. **Backup при каждом сохранении core.json**: Профиль — критичные данные, всегда создавать backup.

8. **Editorials — удобство, не источник истины**: Наличие файлов в editorials/ не означает что тема "изучена". Источник истины — confidence и session_history в profile.

9. **Миграция из v1**: Скрипт `scripts/migrate_profile.py` конвертирует старый profile.json в core.json + stats.json.

10. **Интеграция существующих команд**: Не дублировать логику `/topic_analysis` и `/solve_problem`, использовать их внутри `/coach`.

11. **Drill не влияет на confidence**: Drill — тренировка мышечной памяти, не изучение темы. Confidence обновляется только через learning и practice.
