Diode — терминальный текстовый редактор (клон VS Code) на TypeScript/Node.js. Цели и неизменяемые ограничения — в GOAL.md, не забывай его читать.
- GOAL.md — цели проекта и non-negotiable constraints
- docs/VISION.md — позиционирование: активы, ограничения реальности, направления, текущая ставка
- docs/ARCHITECTURE.md — концептуальная карта: обзор слоёв + правила зависимостей (единственный источник правды по слоям)
- docs/arch/ — детальный per-layer справочник (по файлу на слой: Common.md, Editor.md, Extensions.md, …)
- docs/DI.md — справочник по DI-контейнеру (токены, модули, профили)
- docs/arch/State.md — машинное состояние UI/сессии (StateService, аналог IStorageService/Memento)
- TUIDom (движок TUI) — отдельный репозиторий github.com/tuidom/tuidom, пакеты
@tuidom/*(core, elements, terminal-backend, headless-backend, inspector, testing); его доки (LAYOUT.md, STYLES.md, arch/) живут там - docs/TESTING.md — как тестировать каждый слой; скриншот-демо для визуальных фич
- docs/COMMITS.md — правила коммитов (Conventional Commits, changelog)
- docs/PR.md — скриншот-демо для визуальных фич и как приложить PNG к PR
- docs/TODO/ — трекер задач (индекс — README.md)
Текущие задачи и планы ведём в docs/TODO/. Индекс — docs/TODO/README.md, крупные задачи — в отдельных файлах.
Когда берёшь задачу в работу, поменяй статус на [~]. Когда закончил — на [x].
Любую задачу, которую мы начинаем с этапа планирования, ведём в отдельном git worktree — заходи в него через инструмент EnterWorktree до начала работы. Исключение одно: если пользователь явно сказал не использовать worktree для этой задачи.
Все worktree живут только в .claude/worktrees/<имя> (дефолтная раскладка EnterWorktree). Не создавай их где-либо ещё — ни в соседнем ../<repo>.worktrees/, ни в произвольном каталоге. Каталог .claude/worktrees/ в .gitignore, поэтому рабочее дерево репозитория не засоряется.
В итоге по задаче всегда указывай полный путь к worktree, где она сделана — пользователь оттуда запускает и проверяет.
Перед началом работы прочитай docs/ARCHITECTURE.md — там описана структура каталогов, слои и правила зависимостей. Если ты перемещаешь файлы, добавляешь новые каталоги или меняешь зависимости между слоями — обнови docs/ARCHITECTURE.md.
Не запускай и не исправляй ошибки линтера. Просто не забывай использовать расширения в имени файла при импорте Приватные переменные давай не будем писать с подчеркиванием. Используй просто название и модификатор
- Правила коммитов (Conventional Commits, что попадает в changelog) — docs/COMMITS.md.
- Скриншот-демо для визуальных фич и как приложить PNG к PR — docs/PR.md. Любая фича с видимой/внешней составляющей обязана принести сценарий-демо и скриншот в PR. Это не формальность: в #194 пропущенный сценарий стоил бага, который виден на первом же кадре. Сценарию, которому нужно стоковое расширение, отдай
installVsix(см.e2e/scenarios/regionFolding.scenario.ts).
Урок #194/#195: фича может быть зелёной по тестам и по покрытию, и при этом не работать в приложении совсем. Гейты ниже — про то, чтобы тест смотрел туда же, куда смотрит пользователь.
- Покрытие — не сигнал DoD. Храповик 100% в
vitest.config.tsловит регрессии в написанном коде и по устройству слеп к ненаписанному. Пропущенный продюсер события и недостающее условие в отрисовке дают 100% покрытия и нерабочую фичу. «Добил покрытие» ≠ «фича работает». - Перед сдачей фичи —
npm run test:mutation. Вторая половина храповика: покрытие говорит «строка исполнилась», мутационный балл — «кто-то её проверил». Тест без осмысленного ассерта даёт 100% покрытия и 11% мутационного балла (замерено). Выжившего мутанта либо убиваем тестом, либо гасим// Stryker disable next-line <мутатор>: причина— но не ассертом ради балла. Детали → docs/TESTING.md. - Тестируй наблюдаемый результат, а не шов, который только что написал. Ассерт на структуру, в которую фича пишет (
viewState.foldedRegions), не видит бага в том, кто её читает (рендер). Если у фичи есть видимая часть — тест обязан дойти до кадра. - На каждое новое RPC-сообщение — тест продюсера. «Субпроцесс умеет разобрать сообщение» покрывает половину контракта. Вторая половина — «кто и по какому событию его шлёт»; именно она отсутствовала, и
activeTextEditor.selectionнавсегда залипал на(0,0). - Фича поверх стокового расширения закрывается стоковым расширением. Поднять настоящий
.vsixмало — надо дёрнуть его настоящую функциональность (команду, провайдер) из пользовательского состояния (курсор подвинут, текст выделен) и проверить результат. Фикстура, выведенная из своей же реализации, проверяет ровно то, что и так работает. - Запусти приложение. Харнесс фейкает рендер и ввод — то есть ровно те две области, где баги и живут. Перед «готово» — реальный запуск (
npx tsx src/vs/diode/main.ts --user-data-dir=<tmp> --headless=WxH --inspect-tui=…) и взгляд на кадр.
Сквозные конвенции, которые надо держать в голове. Полные формулировки — в детальных arch-доках по ссылкам.
- Ограничения tuidom НЕ обходим — останавливаемся. Движок приходит пакетами
@tuidom/*из npm; если фиче не хватает возможности движка или виджета (нет API/хука, поведение не параметризуется, виджет не расширяется штатным наследованием с нашей стороны) — это стоп-сигнал: разработку фичи в diode приостановить и поднять вопрос пользователю; недостающее делается в репозитории tuidom (/workspaces/tuidom/tuidom, github.com/tuidom/tuidom) и приезжает новой версией пакета. Запрещённые обходы: копировать куски движка в diode, monkey-patching и лазанье в приватные поля (as any,@ts-expect-error), дубликат виджета у нас ради недостающего хука, пост-обработка отрендеренного grid'а в обход каскада. Тест-обходы — тоже обходы. - Цвета — виджеты и компоненты ссылаются на ИМЕНА токенов темы в
style/styleVar(палитру кладёт в корневой var-scope WorkbenchComponent); прямые чтенияtheme.getColor/getRequiredColor— только для не-виджетных потребителей (токен-темы синтаксиса, декорации). Никаких RGB-литералов и инлайн-фоллбэков; дефолты токенов tuidom — единственно вdom/styles/styleTokens.tsпакета@tuidom/core(репозиторий tuidom). Новый цвет — определение{ defaults: { dark, light }, description }в группе своей области вsrc/vs/platform/theme/common/colors/. Детали → STYLES.md (tuidom), docs/arch/Theme.md. - Рост
vscode.d.ts— файл запиннен к тегу upstream (шапка +extensions/VSCODE_VERSION, держи в лок-степе). Активная поверхность — это дословно раскомментированные строки нижней (закомментированной) копии; единственная ручная правка файла — снятие//, ничего не сужать/переписывать/переоформлять. Тянет незакомментированный тип — раскомментируй и его, либо отложи блок; для «тяжёлых» блоков допускается bounded member-level uncommenting (раскомментировать подмножество членов). Дормантную часть регенерируетscripts/import-vscode-dts.mjs; апдейт upstream — ручной трёхсторонний merge против запинненной базы. Детали → docs/arch/Extensions.md. - Перенесённый diff-движок —
src/vs/editor/common/diff/, частьsrc/vs/editor/common/core/иsrc/vs/base/common/charCode.ts— это дословная копия upstream vscode, а не наш код. Правится только черезscripts/import-vscode-diff.mjs(сменой пина или объявленной там трансформации), ручные правки запрещены — их снесёт ближайшая регенерация, а--checkпокажет дрейф. Шимы вbase/common(arrays,assert,errors,map,strings, …) — наоборот, наш код: расширяются руками по мере надобности и покрываются тестами. Детали → docs/ARCHITECTURE.md, план фичи → docs/TODO/Diff.md. - DI — токены именуются
*DIToken; токен объявляется рядом со своим типом (слой токена = слой типа, проверяетvalid-layers-check);tuidom/токенов не объявляет иdiContainerне импортирует. Детали → docs/ARCHITECTURE.md. - Система команд — ID команд, отражающих VS Code Workbench/Editor, именуются в стиле VS Code; доступность — через typed when-контексты из
ContextKeys.ts. Детали → docs/arch/Workbench.md.
Раскладка — vscode-канон src/vs/* (две оси: слои base→tui→base/browser→platform→editor→workbench→diode и окружения common/browser/node; карта — docs/ARCHITECTURE.md, проверка — npm run valid-layers-check). Имена файлов — camelCase, как у vscode: tuiElement.ts, menuRegistry.ts; файл называется по главному экспорту.
Файлы с тестами не должны быть слишком большими — у нас может быть много кейсов, поэтому тесты пишем не в одном файле, колокацией рядом с кодом:
Если файл один, то структура такая:
tuiElement.ts tuiElement.test.ts
А если больше, то можно делать подразделы
tuiElement.ts tuiElement.events.test.ts