Языковые правила лежали внутри скилла tasks: англицизмы, неизвестные термины, «сложность формулировки — не признак сложности работы». Три пункта из практики, без общей опоры и без ответа на «а что ещё сюда относится». Дом у языка теперь один — canon/references/language.md. Не в tasks, хотя пришли правила оттуда: они относятся к документам канона, решениям ADR, запискам разведки и сообщениям коммитов в той же мере, что к задачам, а каталог задач и сам часть docs/. Раскладка отвечает, где текст лежит, — этот файл отвечает, каким он должен быть словами. Основа — информационный стиль Ильяхова, взятый не целиком. Взято: полезное действие, глагол вместо отглагольного существительного, активный залог, факт вместо оценки, стоп-слова, одна мысль — одно предложение, параллельность, работающий заголовок. Отброшенное названо вслух, и это отдельный раздел. Инфостиль написан для текстов, где читателя надо удержать, а проектный текст читают потому, что надо. Парцелляция ломает причинную связь, а в решении ценность именно в ней. Запрет вводных целиком режет «если» и «в отличие от» — условия, то есть сведения. Скобки в технической записи несут уточнение: имя команды, единицы, слаг. Без этого раздела правило читается как «пиши короче», и первый же агент начинает резать «поэтому» и «иначе». «Снять корону с себя и надеть на клиента» переведено на здешнего читателя: клиент — ты сам через квартал и тот, кто возьмёт задачу. Таблицы англицизмов и жаргона взяты из скилла prepare-jira-text и дополнены; в устав агента они уехали помеченной копией. Устав обязан быть самодостаточным — он не разрешает пути плагина и не ходит по ссылкам, — а два дома у одного правила здесь уже трижды расходились. scripts/copies.py считает теперь 4 копии при 4 домах. У агента вычитки правил стало двенадцать, разделены на форму записи (только для задач) и язык (для любого проектного текста). Находки докладываются в этом порядке: форма меняет решение «брать или не брать», язык — только цену чтения. DECISIONS тема 21 (ЛЛЛ–ООО, следствия 86–88), changelog канона v3 — пункт 6 и шаг переезда «прочитать и ничего не переписывать задним числом». Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
335 lines
21 KiB
Markdown
335 lines
21 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`. Там же живёт язык проектных текстов —
|
||
информационный стиль, англицизмы, жаргон;
|
||
- `docs` — содержимое канона по ходу разработки: ADR из архивного
|
||
`design.md`, промоут конвенций, запись в разведку и журнал ревью, чистка
|
||
архитектуры;
|
||
- `tasks` — задачи и цели каталогом markdown-файлов; вычитку формулировок
|
||
ведёт отдельный агент `task-wording`;
|
||
- `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<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 тоже держит строгую структуру. Определение —
|
||
[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/<capability>/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 манифест маркетплейса
|
||
<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 видит окружение, где нет ничего кроме
|
||
линтеров, и любой сторонний импорт у него не разрешается. Список запретов —
|
||
не перечень мира, настоящий страж второй.
|
||
|
||
`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:
|
||
|
||
```
|
||
<!-- дом: <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, а не молчаливый успех. Прогон занимает секунды на блок,
|
||
поэтому он не в гейте, а в руках того, кто правит диаграмму.
|
||
|
||
Чего проверка **не** ловит — расхождение диаграммы с прозой вокруг неё. Дословного
|
||
соответствия между текстом и графом нет, сличать нечего, и держится это
|
||
правилом старшинства, записанным рядом с каждой диаграммой: **в конвейере ревью
|
||
старший граф** (он и есть алгоритм планировщика, проза его объясняет), **в
|
||
остальных местах старшая проза** (диаграмма там сводка).
|