# Структура проекта

Проект плагина — это каталог с манифестом `catalib.toml` и каталогом исходников (по умолчанию `src/`).

## Минимальный проект

```
my_plugin/
├── catalib.toml
└── src/
    └── plugin.py        # модуль точки входа с подклассом CatalibPlugin
```

## Каталог `src` как пакет

catalib трактует каталог `src` как **пакет, корнем которого является модуль `<plugin_id>`**. Файлы внутри становятся подмодулями `<plugin_id>.<имя>`:

| Файл в `src/`          | Имя модуля в рантайме                |
| ---------------------- | ------------------------------------ |
| `src/__init__.py`      | `<plugin_id>` (тело верхнего модуля) |
| `src/plugin.py`        | `<plugin_id>.plugin`                 |
| `src/util.py`          | `<plugin_id>.util`                   |
| `src/core/__init__.py` | `<plugin_id>.core`                   |
| `src/core/parser.py`   | `<plugin_id>.core.parser`            |

`src/__init__.py` необязателен. Если он есть, его код исполняется как тело верхнего модуля при загрузке плагина.

## Относительные импорты

Внутри плагина используйте **относительные импорты** — они работают благодаря встроенному загрузчику:

```python
# src/plugin.py
from .core.parser import parse
from .services.notes_service import NotesService
from . import config
```

Абсолютные импорты вида `import <plugin_id>...` тоже работают, но относительные не зависят от `plugin_id` и переносимы.

## Обязательное правило: `__init__.py`

**Каждый каталог, содержащий модули, обязан иметь `__init__.py`.** Иначе сборка завершится ошибкой:

```
DiscoveryError: каталоги с модулями без __init__.py: core
```

Это гарантирует предсказуемые пакеты и совпадение поведения с встроенным загрузчиком. `catalib init` создаёт корректную структуру.

## Точка входа

Модуль точки входа задаётся в `catalib.toml`:

```toml
[build]
entry = "plugin"          # -> src/plugin.py
```

В нём должен быть **ровно один** подкласс `CatalibPlugin` (или `base_plugin.BasePlugin`). catalib находит его по модулю определения и поднимает на верхний уровень собранного файла, чтобы exteraGram его нашёл. Импортированные базовые классы (`CatalibPlugin` и т. п.) не считаются — учитывается только класс, **определённый** в модуле точки входа.

## Рекомендуемая раскладка по слоям

Для нетривиальных плагинов:

```
src/
├── __init__.py
├── plugin.py              # точка входа: хуки, меню, настройки
├── config.py              # константы
├── core/                  # доменное ядро (без Android)
├── services/              # бизнес-логика
├── storage/               # персистентность
├── domain/                # модели данных
├── ui/                    # схема настроек
└── util/                  # утилиты
```

Доменные слои не зависят от Android и тестируются офлайн через `pytest`. Полный пример такой раскладки — [разбор exteraToolbox](/catalib/primery/toolbox.md).

## Что не попадает в сборку

* Каталоги `__pycache__` и скрытые (начинающиеся с точки) пропускаются.
* Файлы вне `src` (включая символьные ссылки наружу) отклоняются как попытка path traversal.
* Каталог `tests/` не входит в `src` и не бандлится — это офлайн-тесты.

## Тесты проекта

`catalib init` создаёт `tests/` и `pyproject.toml` с настройкой `pytest` (`pythonpath = ["."]`), поэтому доменные модули импортируются как `from src.<...> import ...` и тестируются без устройства.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://raito-kyokai.gitbook.io/catalib/rukovodstvo/project-structure.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
