Плагин был помечен устаревшим решением Q и жил до перевода jellybit. Удалён раньше этого срока: условие пережило свою причину. Заморозка выглядела бесплатной, а платила собой в каждой проверке репозитория — exclude в pyproject.toml, SKIP_DIRS в copies.py, два абзаца README, оговорка в описании маркетплейса, чтобы не ловить триггер «добавь задачу в беклог». Пять исключений ради 706 строк, которые никто не читает, и каждое надо объяснять всякий раз, когда спрашивают, почему проверка обходит каталог. Причина условия отпала раньше названного срока. docs/backlog/ читает не backlog.py, а av-dev-pm:tasks — adopt.md и адаптер в tasks.py держат ту же раскладку как вход миграции. Плагин перестал быть единственным, кто её знает, ещё когда писался adopt, и «живёт до перевода последнего проекта» с тех пор охраняло пустоту. Перевод jellybit на канон это не задевает. Порядок вышел обратный ожидаемому: плагин удалён из маркетплейса, а с jellybit снят после. Ожидалась ручная чистка enabledPlugins и installed_plugins.json, но claude plugin uninstall отработал штатно — он идёт по реестру, а не по манифесту маркетплейса. Раздел «Снятие» в README переписан с частного случая на общую процедуру, предупреждение заменено проверенным фактом. Записи Q и HH получили парные статусы, тема 30 в DECISIONS несёт причины и три следствия, пункт 5 TODO отмечен, открытый вопрос из REMAINING убран. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
322 lines
20 KiB
Markdown
322 lines
20 KiB
Markdown
# 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`. Там же живёт язык проектных текстов —
|
||
информационный стиль, англицизмы, жаргон. Смысловую часть, которой скрипт
|
||
не видит, судят два агента: `doc-consistency` (документы между собой и с
|
||
openspec) и `doc-code-drift` (документы против кода);
|
||
- `docs` — содержимое канона по ходу разработки: ADR из архивного
|
||
`design.md`, промоут конвенций, запись в разведку и журнал ревью, чистка
|
||
архитектуры;
|
||
- `tasks` — задачи и цели каталогом markdown-файлов, у каждой записи тип
|
||
(`goal`, `feature`, `fix`, `chore`, `research`), и тип задаёт её схему;
|
||
вычитывают их два отдельных прохода: `task-form` (форма записи) и
|
||
`doc-wording` (язык);
|
||
- `session` — ритуал между спринтами и ведение спринта.
|
||
- **av-dev-pipeline** — исполнение. **Требует OpenSpec.**
|
||
- `task-pipeline` — задача через полный цикл SDD, от постановки до коммита;
|
||
- `task-batch` — несколько задач разом, каждая в своём worktree;
|
||
- `review-pipeline` — конвейер ревью: гейт, сверка со спеками, враждебные
|
||
постановки, эксплуатационный постмортем, независимая реализация,
|
||
архитектура, обязательный триаж. Девять агентов-проходов, четыре ступени
|
||
стоимости: `quick`, `standard`, `wide`, `deep`.
|
||
- **av-dev-git** — `commit`: сообщения в личном стиле.
|
||
|
||
Соглашение об именах: имя **плагина** длинное с префиксом `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<br/>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:* — внешний плагин:<br/>explore, propose, apply, archive"]
|
||
git["av-dev-git: commit"]
|
||
|
||
tp --> opsx
|
||
tp --> git
|
||
tp --> docs
|
||
tp --> tasks
|
||
```
|
||
|
||
Зависимость **односторонняя: `av-dev-pipeline` знает про `av-dev-pm`, обратно —
|
||
нет.** Управление продуктом работает в проекте без конвейера; конвейер без
|
||
канона деградирует поразрядно и говорит об этом строкой.
|
||
|
||
## Канон документов проекта
|
||
|
||
Все проекты приводятся к одной раскладке — так проще ориентироваться, когда
|
||
проектов много, и рядом OpenSpec тоже держит строгую структуру. `CLAUDE.md` плюс
|
||
`docs/` (паспорт, архитектура, база, безопасность, конвенции, разведка, ADR,
|
||
ревью, задачи) и `openspec/` — **раскладка целиком, роли документов и правило
|
||
единственного дома живут одним домом**:
|
||
[canon.md](av-dev-pm/skills/canon/references/canon.md). Здесь она не
|
||
пересказывается: копия перечня путей уже расходилась с домом, и как раз в
|
||
обязательных — в ней не хватало путей, чьё отсутствие `docs.py check` считает
|
||
нарушением.
|
||
|
||
**Отдельного файла-брифа для ревью нет.** Проходы читают документы канона
|
||
напрямую; карта «что нужно проходу → где лежит» —
|
||
[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** — работающая сессия
|
||
держит скиллы в контексте и про новый снимок не знает.
|
||
|
||
## Снятие
|
||
|
||
Действие, обратное подключению.
|
||
|
||
```bash
|
||
cd /path/to/project
|
||
claude plugin uninstall <плагин>@av-dev-skills --scope project
|
||
```
|
||
|
||
Команда правит два места: убирает строку из `enabledPlugins` в
|
||
`.claude/settings.json` проекта и запись из реестра
|
||
`~/.claude/plugins/installed_plugins.json`. Снимок в
|
||
`~/.claude/plugins/cache/av-dev-skills/<плагин>/<версия>/` не трогает — он
|
||
общий для всех проектов. Маркетплейс тоже остаётся: `extraKnownMarketplaces`
|
||
нужен остальным плагинам.
|
||
|
||
**Удалять из маркетплейса можно и до снятия с проектов.** `uninstall` идёт по
|
||
реестру, а не по `marketplace.json`, и снимает плагин, записи о котором в
|
||
манифесте уже нет. Проверено на `av-dev-backlog`: удалён из маркетплейса,
|
||
снят с jellybit после — команда отработала штатно.
|
||
|
||
`--scope project` обязателен по той же причине, что и при установке: умолчание у
|
||
команды — user. `cd` в проект обязателен, но здесь ошибка слышна — вызванная не
|
||
оттуда, команда откажется словами `is not installed in project scope`, а не
|
||
снимет плагин наугад, как это делает `plugin update`.
|
||
|
||
**Убрать строку из `settings.json` руками — половина дела:** запись в реестре
|
||
переживает такую правку, и в инвентаризации проект продолжает числиться. Лечится
|
||
той же командой из каталога проекта — она отработает и когда в `settings.json`
|
||
уже пусто.
|
||
|
||
Снялось или нет — видно инвентаризацией из раздела [Обновление](#обновление).
|
||
**Применяется после перезапуска Claude Code**, как и обновление.
|
||
|
||
## Структура репозитория
|
||
|
||
```
|
||
.claude-plugin/marketplace.json манифест маркетплейса
|
||
<plugin>/.claude-plugin/plugin.json манифест плагина
|
||
<plugin>/skills/<skill>/SKILL.md скилы (авто-обнаружение)
|
||
<plugin>/skills/<skill>/references/ что читается по ссылке из скилла
|
||
<plugin>/skills/<skill>/scripts/ tasks.py, docs.py
|
||
<plugin>/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 видит окружение, где нет ничего кроме
|
||
линтеров, и любой сторонний импорт у него не разрешается. Список запретов —
|
||
не перечень мира, настоящий страж второй.
|
||
|
||
## Проверка фронтматтеров
|
||
|
||
Фронтматтер читает не человек, а загрузчик: по `name` он разрешает вызов, по
|
||
`description` решает, звать ли скилл вообще. **Ошибка здесь не выглядит
|
||
ошибкой** — тем же способом, что и в диаграммах.
|
||
|
||
```
|
||
uv run python scripts/frontmatter.py # 0 в порядке, 1 расхождение, 3 не тот каталог
|
||
```
|
||
|
||
Ловится три класса:
|
||
|
||
- **двоеточие с пробелом в описании без кавычек.** Для YAML `: ` внутри
|
||
простого скаляра начинает вложенное отображение, так что «конвейер ревью: гейт,
|
||
сверка…» — это не текст с двоеточием, а синтаксическая ошибка. Так было
|
||
написано часть описаний плагинов, и читались они правильно — замер и разбор
|
||
в [DECISIONS.md](DECISIONS.md), решение III;
|
||
- **`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:
|
||
|
||
```
|
||
<!-- дом: <id> --> …текст… <!-- /дом: <id> -->
|
||
<!-- копия: <id> из <путь к дому> --> …тот же текст… <!-- /копия: <id> -->
|
||
```
|
||
|
||
Идентификатор — буквы, цифры и дефис, и он повторяется в закрывающем маркере.
|
||
Строгость нужна ровно затем, чтобы этот абзац сам не объявил дом: `<id>` под
|
||
шаблон не подходит.
|
||
|
||
Сверяется текст между маркерами; ограда блока кода и пустые строки по краям в
|
||
сверку не входят. Маркер, уехавший в проект вместе со скелетом, там полезен: он
|
||
говорит, что у текста есть дом и правится он там.
|
||
|
||
Скрипт ловит четыре вещи: копия разошлась с домом (с диффом), копия указывает не
|
||
на тот файл, дом остался без копий, разметка сломана. Чего он **не** ловит —
|
||
копию, которую забыли пометить: помечать — по-прежнему решение человека.
|
||
|
||
## Проверка диаграмм
|
||
|
||
Диаграммы `mermaid` живут исходником в markdown — картинок в репозитории нет.
|
||
Синтаксическая ошибка в блоке **не видна при чтении**: текст выглядит
|
||
правдоподобно, диff показывает разумную строку, а рендер падает.
|
||
|
||
```
|
||
uv run python scripts/diagrams.py # 0 рендерятся, 1 нет, 3 нет mermaid-cli
|
||
```
|
||
|
||
Рендерит `mmdc` с PATH или `npx --yes @mermaid-js/mermaid-cli`; ни того ни
|
||
другого нет — код 3, а не молчаливый успех. Прогон занимает секунды на блок,
|
||
поэтому он не в гейте, а в руках того, кто правит диаграмму.
|
||
|
||
Чего проверка **не** ловит — расхождение диаграммы с прозой вокруг неё. Дословного
|
||
соответствия между текстом и графом нет, сличать нечего, и держится это
|
||
правилом старшинства, записанным рядом с каждой диаграммой: **в конвейере ревью
|
||
старший граф** (он и есть алгоритм планировщика, проза его объясняет), **в
|
||
остальных местах старшая проза** (диаграмма там сводка).
|