diff --git a/content/docs/code-design.es.mdx b/content/docs/code-design.es.mdx
index 2b58295e..80eb0cf2 100644
--- a/content/docs/code-design.es.mdx
+++ b/content/docs/code-design.es.mdx
@@ -69,7 +69,7 @@ graph TB
Cli("cli/ y mcp/
comandos y herramientas MCP")
Services("services/
casos de uso")
Sends("sends/
escrituras protegidas, adjuntos")
- Port("ports/messenger.ts
el contrato del adaptador")
+ Port("ports/
el contrato del adaptador, los rasgos")
Store("store/
MessageStore sobre SQLite")
Domain("domain/
modelos y tipos de valor")
end
@@ -89,10 +89,10 @@ graph TB
| Capa | Su función | Tamaño | Qué no puede importar |
| --- | --- | --- | --- |
-| `domain/` | Tipos independientes del mensajero: `Chat`, `Message`, `Person`, localizadores, tramos de formato. Ningún comportamiento que necesite red o archivos. | 24 archivos | Ninguna biblioteca de mensajero ni adaptador. |
-| `ports/messenger.ts` | El contrato que implementa cada mensajero. | 1 archivo, 663 líneas | Lo mismo. |
+| `domain/` | Tipos y valores independientes del mensajero: `Chat`, `Message`, `Person`, localizadores, tramos de formato, los conjuntos de valores de hechos, memoria y tareas. Ningún comportamiento que necesite red o archivos. Está casi abajo del todo: toma dos tipos de `search/` y los tipos de tarea de `tasks/`. | 29 archivos | `cli/`, ninguna biblioteca de mensajero ni adaptador. |
+| `ports/` | El contrato que implementa cada mensajero (`messenger.ts`), lo que un servicio lee del mensajero (`traits.ts`) y quién es la aplicación (`app.ts`). | 3 archivos | Lo mismo. |
| `store/` | El archivo local: una interfaz `MessageStore`, abierta por `openStore`, sobre módulos SQLite, migraciones e índices de búsqueda. | 101 archivos | Nada fuera de `store/` puede importar un módulo de `store/sqlite/`. |
-| `services/` | Casos de uso: mensajes, chats, personas, búsqueda, vectores, menciones, moderación. | 81 archivos | Commander, un módulo de comando, un módulo SQLite. |
+| `services/` | Casos de uso: mensajes, chats, personas, búsqueda, vectores, menciones, moderación. | 87 archivos | Commander, cualquier cosa de `cli/`, un módulo SQLite. Lo mismo vale para `replies/`, `attachments/`, `moderation/`, `models/` y `analysis/`. |
| `sends/` | Toda escritura: la protección de envíos, los identificadores de operación, el registro de envíos, los adjuntos. | 14 archivos | Lo mismo que los servicios. |
| `cli/`, `mcp/` | Presentación: comandos, el sobre JSON, configuración y perfiles; herramientas MCP sobre los mismos servicios. | 168 + 51 archivos | Un módulo SQLite. |
| `meetings/`, `tasks/` | Módulos autónomos con sus propios puertos. | 46 + 4 archivos | Cualquier otra parte de cli-core, una base de datos o el sistema de archivos. |
@@ -164,11 +164,17 @@ misma manera, nunca se descarta en silencio.
### La descripción
-Cada herramienta entrega también al núcleo un objeto `Messenger` de 47 campos: el nombre de la aplicación,
-cómo conectarse, cómo paginar el historial y opciones como `pollQuiz`, `topicShow` y `stickers`. Los
-comandos leen esas opciones al registrar sus parámetros, así que `polls create --quiz` solo existe en una
+Cada herramienta entrega también al núcleo un objeto `Messenger`: el nombre de la aplicación, cómo
+conectarse, cómo paginar el historial y opciones como `pollQuiz`, `topicShow` y `stickers`. Los comandos
+leen esas opciones al registrar sus parámetros, así que `polls create --quiz` solo existe en una
herramienta cuyo mensajero tiene cuestionarios.
+Los servicios ven solo una parte. [`MessengerTraits`](https://github.com/WireCatLabs/cli-core/blob/80616b3813ca8264d8f9edca3e8e0fcce7a1e81e/src/ports/traits.ts), en `ports/traits.ts`, reúne los 14 campos que lee un
+servicio, como el nombre de la aplicación, cómo se pagina el historial y si el mensajero busca en su
+servidor. `Messenger` lo amplía con lo que solo usa la CLI: `connect`, la configuración, el gancho de la
+protección de envíos y el diagnóstico de `doctor`. Un servicio no puede llegar a un comando, a un objeto de
+commander ni a una opción que no se le haya dado.
+
### Los servicios y sus dependencias
Un servicio es una función que recibe un objeto de dependencias y devuelve el caso de uso:
@@ -186,8 +192,9 @@ account: () => Promise
Nada se abre hasta que un servicio lo pide, así que una lectura que responde el almacén local nunca se
conecta.
[`servicesFor(deps)`](https://github.com/WireCatLabs/cli-core/blob/a3b5b3c47792d5e2df6529642e907ee32783e66a/src/services/index.ts#L145)
-crea todos los servicios una vez; los comandos y las herramientas MCP los toman de ahí, así que dan la
-misma respuesta y el mismo error para la misma entrada.
+crea los servicios sobre un objeto de dependencias: un comando los crea para su ejecución, una herramienta
+MCP para su llamada. Ambos llaman a las mismas funciones de servicio, así que un caso de uso tiene una sola
+implementación para los dos.
Una herramienta puede sustituir un servicio sin copiar el núcleo. `Messenger.services` recibe los
servicios compartidos y devuelve sustitutos, por ejemplo su propia forma de encontrar un chat para la
@@ -265,7 +272,7 @@ y una regla de lint o una prueba lo mantiene así.
| Lectores de documentos, descriptores de imágenes, modelos de vectores | Pequeño | Registrar un lector o proveedor; elegir un modelo local o remoto. |
| Un proveedor de reuniones | Pequeño | Implementar `MeetingSource` en el paquete del proveedor. |
| Un mensajero nuevo | Moderado | Implementar `MessengerCore` y los grupos que tenga el mensajero, y escribir una descripción. Los comandos, las herramientas MCP, el almacén, la búsqueda y la protección de envíos vienen con el núcleo. |
-| Una interfaz nueva junto a la CLI y MCP | Moderado | Llamar a `servicesFor(deps)` y mostrar la respuesta. Los servicios aún importan algunas utilidades de `cli/` (configuración, utilidades de errores, consultas de listas), que tendrían que moverse con ella. |
+| Una interfaz nueva junto a la CLI y MCP | Moderado | Construir `ServiceDeps`, llamar a `servicesFor(deps)` y mostrar la respuesta. Los servicios no importan nada de `cli/`; la interfaz nueva escribe su propia forma de conectarse y de leer la configuración. |
| El motor de almacenamiento | Difícil | Reimplementar `MessageStore`: unos 140 métodos, incluidas la búsqueda de texto completo, las raíces de palabras y la búsqueda por vectores. El límite se respeta, pero la interfaz es una sola superficie amplia. |
## Cómo se mantiene el diseño
@@ -293,8 +300,12 @@ Convenciones para el código nuevo:
1. **Una función nueva del mensajero** es un grupo opcional en `ports/messenger.ts`, al que se accede con
`capability()` y que se ofrece mediante una opción de la descripción. `MessengerCore` no crece.
-2. **Un caso de uso nuevo** es un servicio que recibe `ServiceDeps`. Lo llaman el comando y la herramienta
- MCP; un archivo de comando no contiene lógica de negocio.
+2. **Un caso de uso nuevo** es un servicio que recibe la dependencia más estrecha que necesita. Un caso de uso
+ que solo lee el almacén recibe la parte que lee (`Pick`, o un almacén y una cuenta), como
+ la mayoría de los servicios de reuniones y de búsqueda: es más fácil de probar y de mover. Uno que además
+ necesita el mensajero, la protección de envíos o la apertura perezosa recibe `ServiceDeps`. De los 87
+ archivos de servicios, 25 reciben solo el almacén y 36 reciben `ServiceDeps`. Lo llaman el comando y la
+ herramienta MCP; un archivo de comando no contiene lógica de negocio.
3. **Una consulta nueva al almacén** es un módulo en `store/sqlite/`, expuesto solo a través de `store.ts`.
Un servicio lee con `accountStore(deps)`; una lectura deliberada de varias cuentas se identifica con
`crossAccount(deps, reason)`.
@@ -311,8 +322,9 @@ Convenciones para el código nuevo:
- **El almacén es una sola interfaz amplia.** `MessageStore` tiene unos 140 métodos sobre un único
`openStore`, y `store.ts` tiene 2042 líneas. Casi todas las funciones lo tocan. Dividirlo solo sirve
siguiendo una costura real: recursos que se abren y cierran por separado.
-- **Los servicios aún saben un poco de la CLI.** Importan la descripción `Messenger` como tipo y algunas
- utilidades de `cli/` en ejecución.
+- **Las partes de `Messenger` que solo usa la CLI reciben un `Command` de commander.** `connect`,
+ `accountOf`, `guard` y `diagnose` leen ellas mismas las opciones del comando. Los servicios no las ven,
+ pero una segunda interfaz tendría que escribir las suyas.
- **El adaptador de tg es una clase grande**: 2312 líneas y unos 110 métodos, legible método a método, pero
es el lugar donde acaba cada función de Telegram.
- **`client/values.ts` de max** tiene 59 exportaciones que usan 20 módulos.
diff --git a/content/docs/code-design.mdx b/content/docs/code-design.mdx
index 31c88dcd..e08b9e92 100644
--- a/content/docs/code-design.mdx
+++ b/content/docs/code-design.mdx
@@ -68,7 +68,7 @@ graph TB
Cli("cli/ and mcp/
commands and MCP tools")
Services("services/
use cases")
Sends("sends/
guarded writes, uploads")
- Port("ports/messenger.ts
the adapter contract")
+ Port("ports/
the adapter contract, the traits")
Store("store/
MessageStore over SQLite")
Domain("domain/
models and value types")
end
@@ -88,10 +88,10 @@ graph TB
| Layer | Its job | Size | What it may not import |
| --- | --- | --- | --- |
-| `domain/` | Messenger-neutral types: `Chat`, `Message`, `Person`, locators, formatting spans. No behaviour that needs a network or a file. | 24 files | Any messenger library or adapter. |
-| `ports/messenger.ts` | The contract every messenger implements. | 1 file, 663 lines | Same as above. |
+| `domain/` | Messenger-neutral types and values: `Chat`, `Message`, `Person`, locators, formatting spans, the fact, memory and task value sets. No behaviour that needs a network or a file. It sits almost at the bottom: it reads two types from `search/` and the task kinds from `tasks/`. | 29 files | `cli/`, any messenger library or adapter. |
+| `ports/` | The contract every messenger implements (`messenger.ts`), what a service reads of the messenger (`traits.ts`) and who the app is (`app.ts`). | 3 files | Same as above. |
| `store/` | The local archive: one `MessageStore` interface, opened by `openStore`, over SQLite modules, migrations and the search indexes. | 101 files | Nothing outside `store/` may import a module under `store/sqlite/`. |
-| `services/` | Use cases: messages, chats, people, search, embeddings, mentions, moderation. | 81 files | Commander, a command module, a SQLite module. |
+| `services/` | Use cases: messages, chats, people, search, embeddings, mentions, moderation. | 87 files | Commander, anything under `cli/`, a SQLite module. The same holds for `replies/`, `attachments/`, `moderation/`, `models/` and `analysis/`. |
| `sends/` | Every write: the send guard, operation ids, the send journal, uploads. | 14 files | Same as services. |
| `cli/`, `mcp/` | Presentation: commands, the JSON envelope, settings and profiles; MCP tools over the same services. | 168 + 51 files | A SQLite module. |
| `meetings/`, `tasks/` | Self-contained modules with their own ports. | 46 + 4 files | Anything else in cli-core, a database or the file system. |
@@ -162,9 +162,14 @@ never dropped.
### The descriptor
-Each tool also hands the core a `Messenger` object of 47 fields: the app's name, how to connect, how to
-page history, and flags such as `pollQuiz`, `topicShow` and `stickers`. Commands read the flags when
-they register their options, so `polls create --quiz` exists only in a tool whose messenger has quizzes.
+Each tool also hands the core a `Messenger` object: the app's name, how to connect, how to page history,
+and flags such as `pollQuiz`, `topicShow` and `stickers`. Commands read the flags when they register their
+options, so `polls create --quiz` exists only in a tool whose messenger has quizzes.
+
+Services see only part of it. [`MessengerTraits`](https://github.com/WireCatLabs/cli-core/blob/80616b3813ca8264d8f9edca3e8e0fcce7a1e81e/src/ports/traits.ts) in `ports/traits.ts` holds the 14 fields a service reads,
+such as the app's name, how history is paged and whether the messenger searches on its server. `Messenger`
+extends it with what only the CLI uses: `connect`, settings, the guard hook and `doctor`'s diagnosis. A
+service cannot reach a command, a commander object or a setting it was not given.
### Services and their dependencies
@@ -181,8 +186,8 @@ account: () => Promise
Nothing opens until a service asks, so a read answered from the local store never connects.
[`servicesFor(deps)`](https://github.com/WireCatLabs/cli-core/blob/a3b5b3c47792d5e2df6529642e907ee32783e66a/src/services/index.ts#L145)
-builds every service once; the commands and the MCP tools take them from there, so both give the same
-answer and the same error for the same input.
+builds the services over one dependency object: a command builds them for its run, an MCP tool for its
+call. Both call the same service functions, so a use case has one implementation for both.
A tool can replace one service without forking the core. `Messenger.services` receives the shared
services and returns replacements, for example its own way of finding a chat for moderation.
@@ -259,7 +264,7 @@ rule or a test keeps it that way.
| Document readers, image describers, embedding models | Small | Register a reader or provider; choose a local or remote model. |
| A meeting provider | Small | Implement `MeetingSource` in the provider's package. |
| A new messenger | Moderate | Implement `MessengerCore` and the groups the messenger has, and write a descriptor. The commands, MCP tools, store, search and send guard come with the core. |
-| A new front end beside the CLI and MCP | Moderate | Call `servicesFor(deps)` and render. Services still import some helpers from `cli/` (settings, failure helpers, list queries), so those move with it. |
+| A new front end beside the CLI and MCP | Moderate | Build `ServiceDeps`, call `servicesFor(deps)` and render. Services import nothing from `cli/`; the front end writes its own way to connect and resolve settings. |
| The storage engine | Hard | Reimplement `MessageStore`: about 140 methods, including full-text, stemming and vector search. The boundary holds, but the interface is one wide surface. |
## Keeping the shape
@@ -286,8 +291,11 @@ Conventions for new code:
1. **A new messenger feature** is an optional group in `ports/messenger.ts`, reached through `capability()`
and offered through a descriptor flag. `MessengerCore` does not grow.
-2. **A new use case** is a service that takes `ServiceDeps`. The command and the MCP tool both call it; a
- command file holds no business logic.
+2. **A new use case** is a service that takes the narrowest dependency it needs. A use case that only reads the
+ store takes the store part it reads (`Pick`, or a store and an account), as most meeting
+ and search services do: it is the easier one to test and to move. One that also needs the messenger, the
+ send guard or lazy opening takes `ServiceDeps`. Of the 87 service files, 25 take only the store and 36 take
+ `ServiceDeps`. The command and the MCP tool both call the service; a command file holds no business logic.
3. **A new store query** is a module under `store/sqlite/`, exposed only through `store.ts`. A service reads
through `accountStore(deps)`; a read across accounts on purpose names itself with
`crossAccount(deps, reason)`.
@@ -303,8 +311,9 @@ Conventions for new code:
- **The store is one wide interface.** `MessageStore` has about 140 methods over one `openStore`, and
`store.ts` is 2,042 lines. Most features touch it. A split is useful only along a real seam: resources
that open and close separately.
-- **Services still know a little about the CLI.** They import the `Messenger` descriptor as a type and a
- handful of runtime helpers from `cli/`.
+- **The CLI-only parts of `Messenger` take a commander `Command`.** `connect`, `accountOf`, `guard` and
+ `diagnose` read the command's options themselves. Services never see them, but a second front end would
+ write its own versions.
- **The tg adapter is one large class**: 2,312 lines and about 110 methods, readable per method but the
place every Telegram feature lands.
- **max's `client/values.ts`** holds 59 exports used by 20 modules.
diff --git a/content/docs/code-design.ru.mdx b/content/docs/code-design.ru.mdx
index b01c8be3..7b7ae6f0 100644
--- a/content/docs/code-design.ru.mdx
+++ b/content/docs/code-design.ru.mdx
@@ -68,7 +68,7 @@ graph TB
Cli("cli/ и mcp/
команды и инструменты MCP")
Services("services/
сценарии")
Sends("sends/
защищённая запись, вложения")
- Port("ports/messenger.ts
контракт адаптера")
+ Port("ports/
контракт адаптера, свойства")
Store("store/
MessageStore поверх SQLite")
Domain("domain/
модели и типы значений")
end
@@ -88,10 +88,10 @@ graph TB
| Слой | Задача | Размер | Что ему нельзя импортировать |
| --- | --- | --- | --- |
-| `domain/` | Типы, не привязанные к мессенджеру: `Chat`, `Message`, `Person`, локаторы, участки форматирования. Никакого поведения, которому нужна сеть или файл. | 24 файла | Библиотеку мессенджера или адаптер. |
-| `ports/messenger.ts` | Контракт, который реализует каждый мессенджер. | 1 файл, 663 строки | То же. |
+| `domain/` | Типы и значения, не привязанные к мессенджеру: `Chat`, `Message`, `Person`, локаторы, участки форматирования, наборы значений фактов, памяти и задач. Никакого поведения, которому нужна сеть или файл. Слой почти нижний: он берёт два типа из `search/` и виды задач из `tasks/`. | 29 файлов | `cli/`, библиотеку мессенджера или адаптер. |
+| `ports/` | Контракт, который реализует каждый мессенджер (`messenger.ts`), то, что сервис читает о мессенджере (`traits.ts`), и кто такое приложение (`app.ts`). | 3 файла | То же. |
| `store/` | Локальный архив: один интерфейс `MessageStore`, который открывает `openStore`, поверх модулей SQLite, миграций и поисковых индексов. | 101 файл | Ничему вне `store/` нельзя импортировать модуль из `store/sqlite/`. |
-| `services/` | Сценарии: сообщения, чаты, люди, поиск, векторы, упоминания, модерация. | 81 файл | Commander, модуль команды, модуль SQLite. |
+| `services/` | Сценарии: сообщения, чаты, люди, поиск, векторы, упоминания, модерация. | 87 файлов | Commander, что угодно из `cli/`, модуль SQLite. То же верно для `replies/`, `attachments/`, `moderation/`, `models/` и `analysis/`. |
| `sends/` | Любая запись: защита отправки, идентификаторы операций, журнал отправок, вложения. | 14 файлов | То же, что сервисам. |
| `cli/`, `mcp/` | Представление: команды, конверт JSON, настройки и профили; инструменты MCP поверх тех же сервисов. | 168 + 51 файл | Модуль SQLite. |
| `meetings/`, `tasks/` | Самостоятельные модули со своими портами. | 46 + 4 файла | Что-либо ещё из cli-core, базу данных или файловую систему. |
@@ -163,10 +163,14 @@ cannot react to a message», если у адаптера его нет. Пар
### Описание инструмента
-Каждый инструмент передаёт ядру ещё и объект `Messenger` из 47 полей: имя приложения, как подключаться,
-как листать историю, и флаги вроде `pollQuiz`, `topicShow` и `stickers`. Команды читают флаги, когда
-регистрируют свои параметры, поэтому `polls create --quiz` есть только в инструменте, где мессенджер
-умеет викторины.
+Каждый инструмент передаёт ядру ещё и объект `Messenger`: имя приложения, как подключаться, как листать
+историю, и флаги вроде `pollQuiz`, `topicShow` и `stickers`. Команды читают флаги, когда регистрируют свои
+параметры, поэтому `polls create --quiz` есть только в инструменте, где мессенджер умеет викторины.
+
+Сервисы видят только часть этого объекта. [`MessengerTraits`](https://github.com/WireCatLabs/cli-core/blob/80616b3813ca8264d8f9edca3e8e0fcce7a1e81e/src/ports/traits.ts) в `ports/traits.ts` — 14 полей, которые читает
+сервис: имя приложения, как листается история, ищет ли мессенджер на своём сервере и другие. `Messenger`
+добавляет к ним то, что нужно только CLI: `connect`, настройки, хук защиты отправки и диагностику для
+`doctor`. Сервис не может дотянуться до команды, объекта commander или настройки, которую ему не передали.
### Сервисы и их зависимости
@@ -184,8 +188,8 @@ account: () => Promise
Ничего не открывается, пока сервис не попросит, поэтому чтение, на которое отвечает локальное хранилище,
никогда не подключается к сети.
[`servicesFor(deps)`](https://github.com/WireCatLabs/cli-core/blob/a3b5b3c47792d5e2df6529642e907ee32783e66a/src/services/index.ts#L145)
-собирает все сервисы один раз; команды и инструменты MCP берут их оттуда, поэтому на одинаковый ввод дают
-одинаковый ответ и одинаковую ошибку.
+собирает сервисы над одним объектом зависимостей: команда — на свой запуск, инструмент MCP — на свой
+вызов. Оба вызывают одни и те же функции сервисов, поэтому у сценария одна реализация на двоих.
Инструмент может заменить один сервис, не копируя ядро. `Messenger.services` получает общие сервисы и
возвращает замены, например свой способ находить чат для модерации.
@@ -262,7 +266,7 @@ sequenceDiagram
| Чтение документов, описание картинок, модели векторов | Мало | Зарегистрировать читатель или поставщика; выбрать локальную или удалённую модель. |
| Поставщик встреч | Мало | Реализовать `MeetingSource` в пакете поставщика. |
| Новый мессенджер | Средне | Реализовать `MessengerCore` и те группы, что есть у мессенджера, и написать описание. Команды, инструменты MCP, хранилище, поиск и защита отправки приходят вместе с ядром. |
-| Новый интерфейс рядом с CLI и MCP | Средне | Вызвать `servicesFor(deps)` и отрисовать ответ. Сервисы всё ещё импортируют несколько помощников из `cli/` (настройки, помощники ошибок, запросы списков), и они переезжают вместе. |
+| Новый интерфейс рядом с CLI и MCP | Средне | Собрать `ServiceDeps`, вызвать `servicesFor(deps)` и отрисовать ответ. Сервисы ничего не импортируют из `cli/`; новый интерфейс пишет свой способ подключаться и читать настройки. |
| Движок хранилища | Сложно | Заново реализовать `MessageStore`: около 140 методов, включая полнотекстовый поиск, основы слов и поиск по векторам. Граница держится, но интерфейс — одна широкая поверхность. |
## Как сохранить устройство
@@ -289,8 +293,12 @@ Biome строит граф импортов для `noImportCycles`, тольк
1. **Новая возможность мессенджера** — необязательная группа в `ports/messenger.ts`, доступная через
`capability()` и предложенная через флаг описания. `MessengerCore` не растёт.
-2. **Новый сценарий** — сервис, который принимает `ServiceDeps`. Его вызывают и команда, и инструмент MCP;
- в файле команды нет бизнес-логики.
+2. **Новый сценарий** — сервис, который принимает самую узкую зависимость, какая ему нужна. Сценарий, который
+ только читает хранилище, принимает ту часть, которую читает (`Pick` или хранилище и
+ аккаунт), как большинство сервисов встреч и поиска: такой проще проверить и перенести. Сценарий, которому
+ нужны ещё мессенджер, защита отправки или ленивое открытие, принимает `ServiceDeps`. Из 87 файлов сервисов
+ 25 принимают только хранилище, 36 — `ServiceDeps`. Сервис вызывают и команда, и инструмент MCP; в файле
+ команды нет бизнес-логики.
3. **Новый запрос к хранилищу** — модуль в `store/sqlite/`, доступный только через `store.ts`. Сервис читает
через `accountStore(deps)`; намеренное чтение по нескольким аккаунтам называет себя через
`crossAccount(deps, reason)`.
@@ -306,8 +314,9 @@ Biome строит граф импортов для `noImportCycles`, тольк
- **Хранилище — один широкий интерфейс.** У `MessageStore` около 140 методов над одним `openStore`, а
`store.ts` — 2042 строки. Его затрагивает большинство функций. Делить его полезно только по настоящему
шву: ресурсам, которые открываются и закрываются отдельно.
-- **Сервисы всё ещё немного знают о CLI.** Они импортируют описание `Messenger` как тип и несколько
- помощников из `cli/` во время работы.
+- **Части `Messenger`, нужные только CLI, принимают `Command` из commander.** `connect`, `accountOf`,
+ `guard` и `diagnose` сами читают параметры команды. Сервисы их не видят, но второму интерфейсу пришлось бы
+ написать свои.
- **Адаптер tg — один большой класс**: 2312 строк и около 110 методов; каждый метод читается легко, но сюда
попадает каждая функция Telegram.
- **`client/values.ts` в max** содержит 59 экспортов, которые используют 20 модулей.