6.1 KiB
created, updated, tags, status
| created | updated | tags | status | |||
|---|---|---|---|---|---|---|
| 2026-05-25 | 2026-05-25 |
|
ready |
Задача: совместимость импорта с форматом Obsidian vault
Цель: пользователь должен выбрать папку personal/psychology/ из Obsidian
и приложение подхватит профиль и все сессии без конвертации.
Репо: ~/Developer/reflect-skip
Затрагиваемые файлы:
Sources/ReflectApp/Services/DocumentImportService.swiftSources/ReflectApp/Services/ProfileManager.swift
Контекст: что сейчас есть в vault
personal/psychology/
├── profile.md ← есть **Имя:** Алексей (добавлено), YAML frontmatter, rich markdown
├── insights.md ← есть, формат: ## Отцовство / ## ADHD с нарративными блоками
├── prompts.md ← не нужен для импорта
└── observations/ ← папка с еженедельными файлами
├── 2026-W20.md
└── 2026-W21.md
Что ожидает приложение сейчас:
UserProfile/
├── profile.md ← строка "**Имя:** Алексей" (теперь есть ✅)
├── insights.md ← есть ✅
└── sessions/ ← ДРУГОЕ имя папки ❌
└── YYYY-MM-DD.md ← ДРУГОЙ формат имени файла (еженедельные vs ежедневные)
Изменения в DocumentImportService.swift
1. Поддержка папки observations/ как алиаса sessions/
В методах validate(), countSessions(), apply() — проверять обе папки.
countSessions(in:) — текущий код:
let sessionsURL = url.appendingPathComponent("sessions")
Заменить на:
let sessionsURL = sessionsFolder(in: url)
Добавить приватный метод:
private func sessionsFolder(in url: URL) -> URL {
let sessions = url.appendingPathComponent("sessions")
let observations = url.appendingPathComponent("observations")
if FileManager.default.fileExists(atPath: observations.path) { return observations }
return sessions
}
2. Обновить validate() — убрать упоминание "sessions" из ошибки
Текущий error description (noProfileFound) упоминает "папку sessions" — заменить на нейтральное:
В выбранной папке нет файла profile.md.
Убедитесь что выбрана папка с данными профиля.
3. Обновить apply() — копировать observations/ → sessions/
В методе apply() строка:
let sessionsSrc = result.sourceURL.appendingPathComponent("sessions")
Заменить на:
let sessionsSrc = sessionsFolder(in: result.sourceURL)
Папка всегда копируется в sessions/ в app Documents (это правильно — внутреннее имя не меняем).
Изменения в ProfileManager.swift
4. Fallback для sessionCount при импортированном vault
Текущий код ищет regex - Сессий проведено: N — vault profile этого не имеет.
Результат: sessionCount возвращает 0, приложение запускает "первую сессию" даже при 2+ импортированных.
Изменить sessionCount — добавить fallback:
var sessionCount: Int {
// Сначала пробуем разобрать из текста профиля
if let content = try? String(contentsOf: profileFileURL, encoding: .utf8),
let range = content.range(of: #"- Сессий проведено: (\d+)"#, options: .regularExpression),
let numRange = String(content[range]).range(of: #"\d+"#, options: .regularExpression),
let count = Int(String(content[range])[numRange]) {
return count
}
// Fallback: считаем фактические файлы в sessions/
let files = (try? FileManager.default.contentsOfDirectory(
at: sessionsDirectoryURL,
includingPropertiesForKeys: nil,
options: .skipsHiddenFiles
)) ?? []
return files.filter { $0.pathExtension == "md" && $0.lastPathComponent != "index.md" }.count
}
Что НЕ нужно менять
- Формат файлов сессий (
YYYY-MM-DD.mdvs2026-W20.md): BM25 поиск иloadRecentSessions()сортируют лексикографически — оба формата работают одинаково хорошо. - Структура profile.md:
loadProfile()отдаёт raw markdown в Claude — богатый vault-формат работает лучше чем простой app-формат. - insights.md:
loadInsights()тоже отдаёт raw markdown — vault-формат с секциями лучше flat-списка. appendSession()/updateProfile()/addPattern(): создают/дополняют нужные секции — при отсутствии секции код добавляет в конец файла. OK.
Acceptance criteria
- Пользователь выбирает папку
~/obsidian/personal/psychology/на iOS - Validation проходит: имя "Алексей", счётчик сессий = 2
- После import в
Documents/UserProfile/:profile.mdскопированsessions/содержит файлы изobservations/insights.mdскопирован
sessionCountвозвращает 2 (файлы2026-W20.md,2026-W21.md)generateIntro()используетhasHistory = trueи показывает контекст из W20/W21
Объём работы
~30 строк кода. Затрагивает 2 файла, нет API изменений, нет UI изменений.