# av-dev-skills Личный маркетплейс плагинов для разработки. Процесс имени не имеет — он и есть `av-dev`. Что решено и почему — [DECISIONS.md](DECISIONS.md). Что осталось сделать — [TODO.md](TODO.md) и [REMAINING.md](REMAINING.md). Как процесс дошёл до текущей формы — [HISTORY.md](HISTORY.md). ## Плагины - **av-dev-pm** — управление продуктом. Владеет всем `docs/`. - `init` — новый проект: интервью по свободному описанию замысла → первичная документация; - `canon` — привести проект к канону документов: `check` / `adopt` / `upgrade`, плюс скрипт `docs.py`; - `docs` — содержимое канона по ходу разработки: ADR из архивного `design.md`, промоут конвенций, запись в разведку и журнал ревью, чистка архитектуры; - `tasks` — задачи и цели каталогом markdown-файлов; - `session` — ритуал между спринтами и ведение спринта. - **av-dev-pipeline** — исполнение. **Требует OpenSpec.** - `task-pipeline` — задача через полный цикл SDD, от постановки до коммита; - `task-batch` — несколько задач разом, каждая в своём worktree; - `review-pipeline` — конвейер ревью: гейт, сверка со спеками, враждебные постановки, эксплуатационный постмортем, независимая реализация, архитектура, обязательный триаж. Девять агентов-проходов, четыре ступени стоимости: `quick`, `standard`, `wide`, `deep`. - **av-dev-git** — `commit`: сообщения в личном стиле. - **av-dev-backlog** — **устарел**, заменён `av-dev-pm`. Живёт до перевода последнего проекта; как снять с проекта — [Снятие](#снятие). Соглашение об именах: имя **плагина** длинное с префиксом `av-dev-`, имена **скилов** внутри — короткие. Вызов выходит вида `/av-dev-<плагин>:<скилл>`. Кто кого зовёт (стрелка — вызов через пространство имён, не импорт): ```mermaid flowchart TB subgraph pipe["av-dev-pipeline — исполнение, требует OpenSpec"] direction LR batch["task-batch"] --> tp["task-pipeline"] tp --> rp["review-pipeline
9 агентов-проходов"] batch --> rp end subgraph pm["av-dev-pm — управление продуктом, владеет docs/"] direction LR init["init"] --> tasks["tasks"] canon["canon"] --> tasks session["session"] --> tasks docs["docs"] end opsx["opsx:* — внешний плагин:
explore, propose, apply, archive"] git["av-dev-git: commit"] tp --> opsx tp --> git tp --> docs tp --> tasks ``` Зависимость **односторонняя: `av-dev-pipeline` знает про `av-dev-pm`, обратно — нет.** Управление продуктом работает в проекте без конвейера; конвейер без канона деградирует поразрядно и говорит об этом строкой. ## Канон документов проекта Все проекты приводятся к одной раскладке — так проще ориентироваться, когда проектов много, и рядом OpenSpec тоже держит строгую структуру. Определение — [av-dev-pm/skills/canon/references/canon.md](av-dev-pm/skills/canon/references/canon.md). ``` CLAUDE.md инварианты с severity, команды, семантика гейта docs/ .pm.json версия канона и пути для проверок passport.md зачем и для кого; чем НЕ является architecture.md как сложено — обзор; окружение и эксплуатация database.md схема хранилища; настройки с числовым значением security.md периметр; недоверенный вход; что вне модели conventions/ как пишем код + что уже механизировано research/ что показала реальность; числа с провенансом adr/ почему — промоут поверх архивных design.md review.md настройка конвейера + журнал дефектов tasks/ цели, беклог, спринт, отклонённое openspec/ specs//spec.md что система делает — нормативно changes/archive/ архив изменений с design.md ``` **Отдельного файла-брифа для ревью нет.** Проходы читают эти документы напрямую; карта «что нужно проходу → где лежит» — [project-facts.md](av-dev-pipeline/skills/review-pipeline/references/project-facts.md). Прийти в старый проект и перевести его на канон — `/av-dev-pm:canon`. Канон версионируется, и проекты повышаются по [журналу версий](av-dev-pm/skills/canon/references/changelog.md). ## Подключение Плагин подключается **на уровне проекта**, чтобы был активен у всех, кто открывает репозиторий. Из терминала, в каталоге проекта: ```bash cd /path/to/project # маркетплейс: один раз на проект. --scope project кладёт его # в extraKnownMarketplaces этого репозитория (см. ниже), без флага — в user claude plugin marketplace add https://git.vakhrushev.me/av/dev-skills.git --scope project # плагины: scope обязателен, умолчание у команды — user, а нам нужен project claude plugin install av-dev-pm@av-dev-skills --scope project claude plugin install av-dev-pipeline@av-dev-skills --scope project claude plugin install av-dev-git@av-dev-skills --scope project ``` Те же команды изнутри Claude Code — со слешем: `/plugin marketplace add …`, `/plugin install … --scope project`. Обе формы дописывают в `.claude/settings.json` проекта то, что можно внести и руками: ```json { "extraKnownMarketplaces": { "av-dev-skills": { "source": { "source": "git", "url": "https://git.vakhrushev.me/av/dev-skills.git" } } }, "enabledPlugins": { "av-dev-pm@av-dev-skills": true, "av-dev-pipeline@av-dev-skills": true, "av-dev-git@av-dev-skills": true } } ``` **При установке в проект, где лежали проектные копии** скиллов и агентов (`.claude/skills/` — голые `task-pipeline`, `review-pipeline`, `task-batch` и с префиксом проекта `<проект>-task-pipeline`, `.claude/agents/<проект>-review-*.md`) — снеси их. Две копии одного скилла расходятся, и побеждает та, что короче названа. ## Обновление **Обновление — два шага, и первого мало.** `marketplace update` тянет git-клон маркетплейса, но снимки плагинов лежат отдельно, в `~/.claude/plugins/cache/av-dev-skills/<плагин>/<версия>/`, и обновляются только командой `plugin update`. Один шаг без второго выглядит как «обновил, а ничего не изменилось» — так и было в первый раз. ```bash # 0. отправить свои коммиты: до клона доезжает только то, что на origin git -C /path/to/dev-skills push origin master # 1. клон маркетплейса claude plugin marketplace update av-dev-skills # 2. снимки плагинов — из каталога проекта, где они установлены cd /path/to/project claude plugin update av-dev-pm@av-dev-skills --scope project claude plugin update av-dev-pipeline@av-dev-skills --scope project claude plugin update av-dev-git@av-dev-skills --scope project ``` **`cd` в проект обязателен, и это не педантизм.** Команда правит запись реестра, а записей столько, во скольких проектах плагин установлен; за вызов двигается **одна**. Запущенная не из проекта, она обновит какую-то из них — наблюдалось: `av-dev-git` стоял в шести проектах, один вызов поднял версию ровно в одном, и не в том, из которого звали. Проекты обновляются поштучно. Что стоит и какой версии — одной командой: ```bash python3 - <<'EOF' import json, pathlib d = json.loads(pathlib.Path.home().joinpath(".claude/plugins/installed_plugins.json").read_text()) for name, entries in sorted(d["plugins"].items()): if "av-dev" not in name: continue for e in entries: print(f"{e['version']:14} {name:30} {e.get('projectPath', e.get('scope', ''))}") EOF ``` Версия — первые 12 знаков хеша коммита этого репозитория, так что сверяется глазами с `git rev-parse HEAD | cut -c1-12`. Команда идемпотентна: на уже свежем плагине скажет `already at the latest version`. **Изменения применяются после перезапуска Claude Code** — работающая сессия держит скиллы в контексте и про новый снимок не знает. ## Снятие Действие, обратное подключению. Актуально для `av-dev-backlog`: плагин устарел, и с каждого проекта снимается по мере перевода задач на канон `docs/tasks/`. **Сначала перевод, потом снятие.** Задачи переводит `/av-dev-pm:canon` (`docs/backlog/` → `docs/tasks/`). Снять плагин раньше — остаться со старой раскладкой и без скилла, который её понимает. ```bash cd /path/to/project claude plugin uninstall av-dev-backlog@av-dev-skills --scope project ``` Команда правит два места: убирает строку из `enabledPlugins` в `.claude/settings.json` проекта и запись из реестра `~/.claude/plugins/installed_plugins.json`. Снимок в `~/.claude/plugins/cache/av-dev-skills/av-dev-backlog/<версия>/` не трогает — он общий для всех проектов. Маркетплейс тоже остаётся: `extraKnownMarketplaces` нужен остальным плагинам. `--scope project` обязателен по той же причине, что и при установке: умолчание у команды — user. `cd` в проект обязателен, но здесь ошибка слышна — вызванная не оттуда, команда откажется словами `is not installed in project scope`, а не снимет плагин наугад, как это делает `plugin update`. **Убрать строку из `settings.json` руками — половина дела:** запись в реестре переживает такую правку, и в инвентаризации проект продолжает числиться. Лечится той же командой из каталога проекта — она отработает и когда в `settings.json` уже пусто. Снялось или нет — видно инвентаризацией из раздела [Обновление](#обновление). **Применяется после перезапуска Claude Code**, как и обновление. ## Структура репозитория ``` .claude-plugin/marketplace.json манифест маркетплейса /.claude-plugin/plugin.json манифест плагина /skills//SKILL.md скилы (авто-обнаружение) /skills//references/ что читается по ссылке из скилла /skills//scripts/ tasks.py, docs.py /agents/ charter'ы сабагентов scripts/ проверки репозитория: копии, диаграммы, фронтматтеры pyproject.toml линтеры скриптов, только для этого репозитория ``` ## Проверка скриптов `tasks.py` и `docs.py` запускаются **где угодно голым `python3` 3.12 без установки чего-либо** — они лежат рядом со скиллами и работают в любом проекте. `pyproject.toml` в корне не меняет этого: он живёт только здесь и держит линтеры, а не зависимости скриптов. ``` uv sync # ставит ruff и pyrefly в .venv, версии прибиты точно uv run ruff check . # правила; --fix для безопасных починок uv run pyrefly check # типы ``` Ноль внешних зависимостей охраняется двумя способами: `banned-api` у ruff ловит частые соблазны по имени, а pyrefly видит окружение, где нет ничего кроме линтеров, и любой сторонний импорт у него не разрешается. Список запретов — не перечень мира, настоящий страж второй. `av-dev-backlog` из проверки исключён намеренно: плагин помечен устаревшим и живёт до перевода последнего проекта, после чего удаляется целиком. Правки в замороженный код — риск без выгоды. ## Проверка фронтматтеров Фронтматтер читает не человек, а загрузчик: по `name` он разрешает вызов, по `description` решает, звать ли скилл вообще. **Ошибка здесь не выглядит ошибкой** — тем же способом, что и в диаграммах. ``` uv run python scripts/frontmatter.py # 0 в порядке, 1 расхождение, 3 не тот каталог ``` Ловится три класса: - **двоеточие с пробелом в описании без кавычек.** Для YAML `: ` внутри простого скаляра начинает вложенное отображение, так что «конвейер ревью: гейт, сверка…» — это не текст с двоеточием, а синтаксическая ошибка. Так было написано три описания из четырнадцати, и читались они правильно; - **`name`, разошедшееся с именем каталога скилла или файла charter'а.** Вызов разрешается по каталогу, а сообщение о промахе говорит «нет такого скилла», а не «имя не то»; - **цвет charter'а, не отвечающий его модели.** Цвет кодирует модель, а не роль прохода — раскладка живёт в [review-pipeline/SKILL.md](av-dev-pipeline/skills/review-pipeline/SKILL.md), разделе «Модель по проходу», здесь только её механизация. Держаться вниманием правило не может: цвет ставится один раз при заведении charter'а, а модель потом меняется калибровкой. ## Проверка копий правил «Один факт — один дом» держалось вниманием и трижды не удержалось. Копии всё же нужны: скелеты канона уезжают в репозиторий проекта и обязаны там что-то говорить. Значит копия допустима, но **дословная и помеченная**: ``` uv run python scripts/copies.py # 0 сошлось, 1 расхождение, 2 разметка, 3 не тот каталог ``` Разметка — HTML-комментарии, невидимые в отрендеренном markdown: ``` …текст… …тот же текст… ``` Идентификатор — буквы, цифры и дефис, и он повторяется в закрывающем маркере. Строгость нужна ровно затем, чтобы этот абзац сам не объявил дом: `` под шаблон не подходит. Сверяется текст между маркерами; ограда блока кода и пустые строки по краям в сверку не входят. Маркер, уехавший в проект вместе со скелетом, там полезен: он говорит, что у текста есть дом и правится он там. Скрипт ловит четыре вещи: копия разошлась с домом (с диффом), копия указывает не на тот файл, дом остался без копий, разметка сломана. Чего он **не** ловит — копию, которую забыли пометить: помечать — по-прежнему решение человека. ## Проверка диаграмм Диаграммы `mermaid` живут исходником в markdown — картинок в репозитории нет. Синтаксическая ошибка в блоке **не видна при чтении**: текст выглядит правдоподобно, диff показывает разумную строку, а рендер падает. ``` uv run python scripts/diagrams.py # 0 рендерятся, 1 нет, 3 нет mermaid-cli ``` Рендерит `mmdc` с PATH или `npx --yes @mermaid-js/mermaid-cli`; ни того ни другого нет — код 3, а не молчаливый успех. Прогон занимает секунды на блок, поэтому он не в гейте, а в руках того, кто правит диаграмму. Чего проверка **не** ловит — расхождение диаграммы с прозой вокруг неё. Дословного соответствия между текстом и графом нет, сличать нечего, и держится это правилом старшинства, записанным рядом с каждой диаграммой: **в конвейере ревью старший граф** (он и есть алгоритм планировщика, проза его объясняет), **в остальных местах старшая проза** (диаграмма там сводка).