# MAST

Method for Agents, Sessions and Tasks — плагин Claude Code, который разворачивает в чужом
проекте наш способ работы: роадмап с пунктами, worktree на пункт, диспетчер, правила с `paths:`,
архив закрытого.

## Куда смотреть

| Документ | Когда читать |
|---|---|
| [ROADMAP.md](ROADMAP.md) | что осталось сделать |
| [docs/roadmap/done/A-1/SPEC.md](docs/roadmap/done/A-1/SPEC.md) | зачем плагин так устроен, ограничения площадки, состав ядра |
| [docs/roadmap/DONE.md](docs/roadmap/DONE.md) | что уже закрыто |
| [TECH_DEBT.md](TECH_DEBT.md) | что оставлено криво и при каком условии чиним |
| [CHANGELOG.ru.md](CHANGELOG.ru.md) | что меняется для пользователя; закрытый пункт дописывает свои строки в раздел готовящегося релиза |

## Стек

- Markdown — тексты ядра, скиллов и шаблонов, по комплекту на язык в `plugins/<язык>/locales/<язык>/`.
- Python 3 (только stdlib) — скрипты хуков и сторожа́.
- pytest — сторожа́ в `tests/`.

Из одного репозитория собирается **два плагина**: `plugins/en` → `mast` и `plugins/ru` →
`mast-ru`. Язык — это выбор плагина при установке, а не настройка внутри одного.

## Команды

```bash
git config core.hooksPath .githooks       # один раз после клонирования
pytest                                    # весь набор
python3 hooks/roadmap_lint.py ROADMAP.md  # проверка формата роадмапа
python3 hooks/roadmap_lint.py --ready ROADMAP.md
python3 tools/sync_plugins.py             # разложить копии hooks/ по plugins/<язык>/
python3 tools/sync_plugins.py --check     # копии отстали от источника? (в pre-commit)
```

`.git/hooks/` не версионируется, поэтому pre-commit едет в `.githooks/` и
подключается этой командой. Не подключил — правку без бампа версии поймает
`tests/test_version_bump.py`, но уже после коммита.

## Живая проверка установки

Маркетплейс-каталог (локальная папка) площадка грузит на месте, без кэша, — он не проверяет
то, что получит пользователь с GitHub. Без push через кэш идёт только git-маркетплейс — его
поднимает скрипт:

```bash
python3 tools/serve_marketplace.py snapshot <каталог>  # до установки: копии общих файлов ~/.claude
python3 tools/serve_marketplace.py                     # до Ctrl+C; --name (по умолчанию mast-local), --port
python3 tools/serve_marketplace.py compare <каталог>   # после уборки: 0 отличий — код 0
```

Скрипт собирает bare-репозиторий из текущего дерева, а не из HEAD (`plugins/` и манифест
с другим именем — `ex3del` занято установленным), отдаёт его по smart HTTP через
`git http-backend` (`file://` и `git://` площадка не принимает, dumb HTTP не умеет
shallow-клон) и печатает команды `claude plugin …`: добавить маркетплейс, поставить и убрать
плагин, выключить `mast-ru@ex3del` — всё `--scope local` во временном проекте с `ROADMAP.md`
(иначе ядро не вкладывается). Плагин должен грузиться из `cache/<маркетплейс>/<плагин>/<версия>`,
маркер ядра («## Где что записывать» / «## Where to write things») — в контексте. `compare`
сверяет `settings.json`, `known_marketplaces.json`, `installed_plugins.json`;
автообновление чужих плагинов — не своё, не чинить.

Хуки — живой сессией. `claude --bg` доверяет только точному корню git (родитель не считается) —
временный проект кладётся в доверенный путь, например `/private/tmp/mast-check-new`. Правку
файлов в основной копии фоновой сессии площадка отклоняет сама, раньше PreToolUse, поэтому
хуки на правки проверяются в `claude -p --name …`, а `AskUserQuestion` (в `-p` его нет) —
в `--bg` (A-13).

Детали прогона — в телах коммитов `8b8f307` и `1a20e4c`.

## Инварианты проекта

- **Плагин не трогает чужое.** Ни `~/.claude/CLAUDE.md`, ни `settings.json` пользователя, ни файлы его проекта без подтверждения. Любая правка существующего файла — только через дифф и вопрос.
- **Ядро в `SessionStart` ≤ 8200 символов** при лимите площадки 10 000. Не влезает — секция уезжает в скилл, а не сокращается до непонятности. Уехала — в ядре остаётся правило одной строкой **и имя скилла в том месте, где оно применяется**: ссылка одним списком в конце не работает, агент не знает, когда её открыть.
- **Два языка равноправны.** Правка текста на одном языке без правки на другом не вливается: сторож локалей сверяет состав файлов и дерево заголовков.
- **Пути внутри плагина — через `${CLAUDE_PLUGIN_ROOT}`.** Никаких `$HOME/.claude/...`: у пользователя плагина такого пути нет. Путь из текста скилла или команды резолвится относительно `plugins/<язык>/`; каждый такой путь проверяет сторож в `tests/test_plugin_manifest.py`.
- **Имя плагина в коде — только из `hooks/plugin_names.py`.** Литерал `mast`/`mast-ru` в хуке расходится с манифестом молча, и пользователь получает ссылку на несуществующую команду или скилл.
- **Числа в «Готово когда» получены замером**, а не переписаны из примера.
- **Плагин самодостаточен, источник один.** Площадка копирует в кэш только содержимое
  `plugins/<язык>/` — проверено живьём на установке из маркетплейса, `../../` из плагина ведёт
  в пустоту. Тексты языка едут ровно в один плагин и живут только в
  `plugins/<язык>/locales/<язык>/` — там и правятся, второй копии нет (сторож в
  `tests/test_locales.py`). Код хуков общий на оба плагина: правится **только** в корневых
  `hooks/`, копии с пометкой «Сгенерировано» раскладывает `python3 tools/sync_plugins.py`,
  а `--check` в pre-commit и сторож в тестах не дают закоммитить отставшую копию или копию
  без пометки. Символьная ссылка вместо копии не годится: git на Windows по умолчанию
  выкладывает её текстовым файлом, и хук падает с `SyntaxError` (проверено в A-18).
- **Версия растёт вместе с методом и одинакова у обоих плагинов.** Правка `hooks/` или
  `plugins/` без бампа — релиз, которого не получит ни один пользователь:
  Claude Code обновляет плагин с явной версией только при её росте.
- **Новое правило метода — это пол или потолок?** Пол — инвариант, который проверяет guard
  (число в критерии, линт, владение путями). Потолок — «как именно и в каком порядке ходить».
  Потолок в ядро и скиллы не вливается: он устареет ровно тогда, когда модель поумнеет,
  и его придётся выдирать.
