---
name: mm-save-session
version: 0.7.0
description: Закрывает текущую Claude Code сессию — сохраняет лог в Obsidian/Claude/Sessions/, обновляет project note, обновляет INDEX.md, перегенерирует handoff.md (для Project Knowledge claude.ai) через /mm-handoff. Если в проекте есть GSD (.planning/ или .gsd/) — также вызывает /gsd-pause-work для technical state в HANDOFF.json. Use when user says "закругляемся", "сохрани", "до завтра", "конец дня", "save session", "/mm-save-session", "закрываемся".
---

# mm-save-session — End-of-Session Logger

Оформляет финал Claude Code сессии в три места: Sessions/, Projects/, INDEX.md. Реализует правила из `~/.claude/CLAUDE.md` как skill (раньше выполнялось вручную, теперь гарантированно).

## Конфиг

Загрузи `mm-config.json` по алгоритму из `<repo>/docs/CONFIG-LOADING.md`. Поддержка `mm-config.local.json` overlay обязательна.

Понадобятся:
- `paths.obsidian_sessions`
- `paths.obsidian_projects`
- `paths.obsidian_index`

## Процесс

### Шаг 1. Собери контекст сессии

**Из текущего разговора:**
- Что обсуждали (тема в 1-2 предложениях)
- Что было сделано (список фактов: что создано, изменено, исправлено)
- Какие решения приняты и почему
- Что осталось / открытые вопросы / следующие шаги

**Из git** (если репо):
```bash
git log --since="6 hours ago" --oneline
git status --short
git diff --stat HEAD~5 2>/dev/null || git diff --stat
```
Список изменённых файлов и коммитов сегодня.

**Из cwd**: имя проекта = имя корневой папки (или из `passport.md` если есть).

### Шаг 1.5. Собери «Точку возврата»

Чтобы следующее возвращение было без раскачки, на **КАЖДОМ** save фиксируй три вещи (команда остаётся одна — никаких отдельных режимов сохранения):

1. **Следующий конкретный шаг** — одно действие, с которого продолжить. Бери из «Открытых вопросов / следующих шагов» этой сессии. Если из разговора он неочевиден — спроси louise одной строкой: `С чего продолжить в следующий раз? (одна строка)`.
2. **Недокоммиченный WIP** — из `git status --short` (уже собран в Шаге 1). Перечисли файлы или `нет, дерево чисто`.
3. **Готовый, но неотправленный промпт** — если в этой сессии был собран промпт для PowerShell-Клода / claude.ai, который так и не выполнили, — запиши его текст целиком. Иначе `нет`.

Это пойдёт в секцию «Точка возврата» файла сессии (Шаг 3) и оттуда — в handoff.md (Шаг 5.6 через `/mm-handoff`).

### Шаг 2. Определи целевые пути

- **Имя проекта**: из `passport.md` (если в cwd или его родителях есть) → frontmatter `project`. Иначе — имя папки cwd.
- **Файл сессии**: `<obsidian_sessions>/<YYYY-MM-DD-HHMM>-<тема-slug>.md`. Время — текущее, тему-slug сгенерируй из 3-5 слов на русском в kebab-case (транслит, нижний регистр).
  - Если файл уже существует — добавь суффикс `-2`, `-3`.

### Шаг 3. Создай файл сессии

```markdown
---
created: <YYYY-MM-DDTHH:MM>
project: <project_name>
project_path: <abs_path>
tags: [claude-session, проект-<project_name>, <topical-tags>]
---

# <Заголовок-человеческий>

**Дата:** <YYYY-MM-DD HH:MM>
**Проект:** [[../Projects/<project_name>|<project_name>]]
**Длительность:** примерно <Xм / Xч>

## Задача сессии
<1-2 предложения что собирались сделать>

## Что сделано
- <факт 1>
- <факт 2>
- <факт 3>

## Ключевые решения
- **<решение>** — <почему>

## Открытые вопросы / следующие шаги
- [ ] <q1>
- [ ] <q2>

## Точка возврата
- **Следующий конкретный шаг:** <одно действие, из Шага 1.5>
- **Если прервались посреди:**
  - Недоделано: <что осталось / «—»>
  - Неотправленный промпт: <текст готового, но не выполненного промпта целиком / «нет»>
  - Недокоммиченный WIP: <файлы из git status / «нет, дерево чисто»>

## Затронутые файлы
- `<rel_path>` — <что>
- `<rel_path>` — <что>

## Git
<если был>
- Ветка: `<branch>`
- Коммитов сегодня: <N>
- `<hash> <subject>`
```

### Шаг 3.5. Scrub секретов ПЕРЕД записью в vault (по SECRET-PATTERNS.md)

> **Заметка сессии оседает в долговременном vault** (и через Шаги 4–6 её выжимка попадает в project note / INDEX / handoff). Токен, случайно вставленный из кода или git-вывода в «Что сделано», утечёт. Поэтому собранный на Шаге 3 текст прогоняется через фильтр **до** записи.

Переиспользуй единый механизм (как `mm-handoff` Шаг 5.9) — не дублируй список паттернов здесь. Прогони весь собранный текст заметки через паттерны из `<repo>/docs/SECRET-PATTERNS.md`:

1. **Класс A** — маскируй **молча**, типизированным плейсхолдером с сохранением контекста (`TELEGRAM_TOKEN=<REDACTED:telegram-token>` и т.п. — см. doc). Не вырезай строку.
2. **Класс B** (широкое `[A-Za-z0-9_\-]{32,}`) — **НЕ маскируй** (задевает git SHA-40 / UUID, которых полно в сессиях). Только добавь одну строку-warn в финальный отчёт.
3. **Покажи preview перед записью**: если что-то замаскировано или есть warn — выведи одну сводную строку (`🔒 Замаскировано N (telegram-token×1, …); ⚠️ K warn`) и сами места (по 1 строке). Если находок нет — короткое `🔒 секретов не найдено`, без шума.
4. В Шагах 3 (запись файла сессии), 4 (project note), 5 (INDEX) используй **уже очищенный** текст — выжимки наследуют scrub, повторно не маскируй.

### Шаг 4. Обнови файлы 00-home/

Путь хранилища: `<vault_root>` (определяй по единому алгоритму: (1) из секции `## Obsidian Knowledge Vault` в `CLAUDE.md` со строкой `Хранилище знаний: <путь>`, если она есть; (2) локальная папка `.vault/` в корне проекта; (3) глобальная папка проекта `<obsidian_projects>/<name>/` из конфига).

1. **Обнови `<vault_root>/00-home/текущие приоритеты.md`**:
   - Перепиши файл, указав текущий milestone/phase и актуальный список задач (WIP и следующие шаги, определенные на Шаге 1.5).

2. **Создай новые заметки в `knowledge/` (если применимо)**:
   - Проанализируй сессию. Если были приняты ключевые решения, исправлены баги или зафиксированы паттерны:
     - **Решения**: Создай файл в `<vault_root>/knowledge/decisions/`. Имя файла должно быть **утверждением**, а не категорией (например, `выбрали Supabase Auth вместо NextAuth потому что...md`). Внутри напиши краткое обоснование (на русском), добавь frontmatter (`tags: [decision]`, `date: <YYYY-MM-DD>`) и wiki-ссылку на сессию: `[[sessions/<session_file_basename>|Лог сессии]]`.
     - **Баги**: Создай файл в `<vault_root>/knowledge/debugging/`. Имя файла должно быть **утверждением** (например, `API возвращает 429 после 500 запросов решено экспоненциальным бэкоффом.md`). Внутри напиши суть проблемы и решение, добавь frontmatter (`tags: [bugfix]`, `date`) и wiki-ссылку на сессию.
     - **Паттерны**: Создай файл в `<vault_root>/knowledge/patterns/`. Имя файла должно быть **утверждением** (например, `все API роуты валидируются через Pydantic.md`). Напиши описание паттерна и ссылку на сессию.
     - Все названия файлов должны оканчиваться на `.md`.

3. **Регенерируй `<vault_root>/00-home/index.md`**:
   - Просканируй папки базы знаний.
   - Сформируй оглавление со ссылками `[[00-home/текущие приоритеты|Текущие приоритеты]]`, `[[atlas/passport|Паспорт проекта]]`, `[[atlas/архитектура проекта|Архитектура]]`, `[[atlas/база данных|База данных]]`, `[[atlas/деплой|Деплой]]`, а также ссылки на все заметки в `knowledge/decisions/`, `knowledge/debugging/`, `knowledge/patterns/`, `knowledge/business/`, `knowledge/integrations/` и последние 5 сессий из `sessions/`.
   - Держи индекс компактным (до 100 строк).

### Шаг 5. Обнови INDEX.md (глобальный)

Путь: `<obsidian_index>` (по дефолту `<obsidian_claude_root>/INDEX.md`).
Добавь строку **наверх** списка сессий:
```
- <YYYY-MM-DD HH:MM> · **<project_name>** · [[Projects/<project_name>/sessions/<file_basename>|<тема>]] (если глобальный) или [[.vault/sessions/<file_basename>|<тема>]] (если локальный)
```
Если INDEX.md нет — создай со скелетом (аналогично старому mm).

### Шаг 5.5. GSD-кооперация (если применимо)

**Цель:** mm и GSD не дублируются. mm пишет нарратив в Obsidian. GSD пишет technical state в `HANDOFF.json`.
Алгоритм:
1. Проверь `<project_root>/.planning/` (GSD v1 / core) или `<project_root>/.gsd/` (v2).
2. Если ни одного — пропусти этот шаг.
3. Если есть — спроси одной строкой: `Также вызвать /gsd-pause-work для technical handoff (HANDOFF.json)? (y/n, дефолт y)`.
4. На `y`:
   - **GSD v1 / Core**: вызови skill `/gsd-pause-work` (в Claude Code/Antigravity; не пиши `.planning/HANDOFF.json` сам — там охраняющий хук).
   - **GSD v2**: предложи юзеру выполнить в терминале `gsd handoff` (skill этот не запускает CLI).
5. На `n` — продолжай без GSD-handoff, упомяни в финальном отчёте: «GSD handoff пропущен по запросу».
6. Если `/gsd-pause-work` вернул ошибку — не падай, упомяни в отчёте: «GSD pause-work failed: <reason>», продолжай.

### Шаг 5.6. Обнови handoff.md (для claude.ai Project Knowledge)

**Цель:** `handoff.md` должен оставаться свежим после каждого закрытия сессии, без ручного `/mm next`.

Алгоритм:
1. Делегируй генерацию в `/mm-handoff` (он — единственный владелец формата handoff; не дублируй его логику здесь). Вызови skill `mm-handoff` — он прочитает только что записанную сессию + последние 3-5 + git + (если есть) GSD и **перезапишет** `<vault_root>/handoff.md`.
2. Это поведение **по умолчанию включено** (пользователь явно его просил) — не спрашивай y/n.
3. Если `<vault_root>/handoff.md` ещё скелет (после `/mm-init-project`) — `/mm-handoff` просто заменит его полноценной версией. Это нормально.
4. Если `/mm-handoff` упал (нет паспорта, vault недоступен) — не падай, упомяни в финальном отчёте: «handoff обновить не удалось: <reason>», сессия всё равно сохранена.

### Шаг 5.7. Автопуш в Vault-репозиторий (если настроен)

Если папка памяти проекта `<vault_root>` является git-репозиторием и в ней настроен remote origin (т.е. vault уже инициализирован через `/mm vault`):
1. **Прогони Secret-Scan** по всем измененным/новым файлам перед добавлением в git (по логике Шага 3.5). Если обнаружен реальный секрет (Класс A), прерви автопуш, выведи предупреждение и не коммить. Блокирует только реальный секрет (Класс A, который человек убирает вручную). Предупреждения Класса B служат только для информирования, НИКОГДА не редактируй содержимое файлов памяти проекта ради сканера (скилл не переписывает текст заметок).
2. Если сканирование чистое:
   - Выполни `git add -A` в директории `<vault_root>`.
   - Выполни коммит: `git commit -m "auto-sync: save session <session_file_basename>"`
   - Выполни пуш: `git push origin main`
   - **Обработка сбоев пуша:** Если команда `git push` завершилась с ошибкой (например, проблемы с сетью, авторизацией или non-fast-forward коммиты), **не прерывай работу скилла** (сессия локально сохранена). Выведи пользователю заметное предупреждение: `⚠️ vault push не прошёл — Knowledge не обновится, запушь вручную.` и укажи причину сбоя.
3. Если `<vault_root>` не является репозиторием или в нем нет настроенного remote, тихо пропусти этот шаг (no-op).

### Шаг 6. Подтверди

Выведи (короткий блок, без воды):

```
✅ Сессия сохранена

Проект: <name>
Директория Vault: <vault_root>
Файл сессии: sessions/<session_file>
Созданы новые знания:
  • <список созданных файлов решений/багов/паттернов с именами-утверждениями или "нет">
00-home/index.md и текущие приоритеты.md обновлены.
handoff.md обновлён: <да | не удалось: <reason>>
GSD handoff: <выполнен | пропущен | n/a>
Vault sync: <пуш выполнен | не настроен (no-op) | ошибка: <reason>>

Открытых вопросов: <K>
Следующий шаг: <дейстие из Точки возврата>
```

## Edge cases

- **Очень короткая сессия** (один вопрос-ответ, ничего не делалось): спроси `Сессия короткая, всё равно сохранять? (y/n)`. Если да — сохрани с пометкой `короткая консультация`.
- **Несколько проектов в одной сессии** (редко): спроси какой считать главным, остальные упомяни в тегах.
- **Нет Obsidian vault по пути из конфига**: останови, скажи `Vault не найден: <path>. Проверь mm-config.json`.
- **Сессия в worktree**: имя проекта бери из основной папки репо, не из имени worktree (типа `crazy-galileo-...`).

## Что НЕ делать

- Не коммить ничего в git-репозиторий самого проекта (это работа `/push` или ручная). Автоматический коммит и пуш разрешены только для vault-репозитория памяти на Шаге 5.7.
- Не делай длинные сессии-хроники — формат компактный, факты + решения + вопросы.
- Не тегируй случайно — теги только из реальных тем сессии.
- Не дублируй текст между Sessions/ и Projects/ — в Project note только ссылка + одна строка.
