Andrey Epifancev aecab7627c fix: typecheck src/core without Node types; pin exact dependency versions (T1.1, O-2)
T1.1: tsconfig.core.json (types: []) covers src/core; typecheck runs both
passes, so process/Buffer in core fail compilation. tests/core-types.test.ts
compiles a fixture with Node globals under both configs and rejects config
errors as false positives. The fixture is excluded from the root tsconfig.

O-2: devDependencies pinned to the locked versions, .npmrc save-exact=true.
Lock updated only in the root specifiers: npm install on macOS would prune
optional rolldown binaries for other platforms.

Refs #3

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKHeGuAYh4W1jNdjMConZa
2026-09-27 20:08:57 +04:00

37 lines
4.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Obsidian Voice Note — плагин голосовых заметок
## Что это
Плагин Obsidian: запись голоса с телефона или десктопа → отправка файлом во внешний сервис распознавания → текст в заметку. Mobile-first: основной сценарий — iPhone, десктоп получает то же.
## Стек и ограничения
- TypeScript, esbuild (стандартный шаблон obsidian-sample-plugin), vitest для тестов.
- `isDesktopOnly: false` в manifest.json. Никакого Node API (`fs`, `child_process`, `path`), только Web API и API Obsidian (`requestUrl`, `app.vault`).
- Запись: `MediaRecorder`. Форматы на входе различаются: iOS даёт audio/mp4, Android — audio/webm, десктоп — webm/ogg. Провайдер должен принимать все три или конвертировать.
- Отправка файлом, без стриминга.
## Архитектура
- `src/core/` — чистый TypeScript без импортов из `obsidian`: машина состояний записи (created → saved → sent → transcribed → inserted, ветки на потерю сети и обрыв записи), очередь отправки, провайдеры распознавания, формирование markdown-заметки. Покрывается vitest полностью.
- `src/obsidian/` — тонкая обёртка: команды, настройки, UI, работа с vault. Юнит-тестов нет, проверяется руками в тестовом vault.
- Провайдер — интерфейс с реализациями: `openai-compatible` (AITUNNEL по умолчанию; Groq, OpenAI, whisper на VPS — тот же код, другой URL) и при необходимости `yandex`. Переключение на следующего провайдера по 403/таймауту.
- Ключи API — в настройках плагина (`data.json`), в git не попадают.
## Команды
- `npm run dev` — сборка в watch-режиме в тестовый vault.
- `npm test` — vitest, вся папка `tests/`. Зелёным считается весь набор.
- `npm run build` — продакшн-сборка `main.js`.
- `npm run typecheck` — два прохода: весь проект с Node-типами и `src/core` через `tsconfig.core.json` без Node-типов (`process`, `Buffer` в core — ошибка компиляции).
- Версии зависимостей точные (`.npmrc`: `save-exact=true`). Lock-файл не перегенерировать через `npm install` на одной ОС: npm вычищает опциональные бинарники других платформ.
## Тесты
- Golden-тесты: `tests/fixtures/<name>.m4a` + `tests/fixtures/<name>.txt` (эталонный транскрипт). Провайдер в тестах мокается; golden-файлы проверяют формирование заметки и обработку ответа, а не сам сервис.
- Тестовый vault: `/Users/andrey.epifancev/Library/Mobile Documents/iCloud~md~obsidian/Documents/voice-note-test` — синхронизируется между Mac и телефоном.
## Первая подсистема
Жизненный цикл записи и очередь (core), затем провайдер, затем обёртка. Тестовая инфраструктура — Task 1 первого плана.
## OpenSpec и superpowers
- Вход в любую задачу — superpowers:brainstorming. Skills openspec-propose и openspec-explore сами по себе не запускать.
- Когда brainstorming классифицировал задачу как architectural, структуру change создать через `/opsx:propose <id>` (или скопировать его шаблон), а содержимое proposal.md, specs/ и design.md написать по итогам brainstorming. tasks.md из OpenSpec не заполнять — задачи живут в плане superpowers.
- `/opsx:apply` не использовать: исполнение идёт через writing-plans и SDD/executing-plans.
- `/opsx:archive <id>` — после merge PR, как часть закрытия.