--- name: commit-message description: >- ОБЯЗАТЕЛЬНО при commit, коммит, закоммить, commit message, conventional commit, staged, semantic-release, «сгенерируй коммит», git commit, /commit-message: ПЕРВЫМ делом Shell — scripts/commit/staged-context.ps1; затем Conventional Commit (заголовок EN, тело RU). --- > **Кнопка ✨ Generate commit message в Source Control** не использует этот скилл и Rules. > Эквивалент: Agent Chat → **`/commit-message`** или «сгенерируй коммит по staged». > См. [docs/README.md](../../docs/README.md#сообщения-коммитов-cursor). # Commit message (EvoBGP) ## Когда применять (сразу читать этот скилл) Триггеры в промпте пользователя или в плане/AGENTS.md: - `commit`, `коммит`, `закоммить`, `commit message`, `conventional commit` - `git commit`, `staged`, `сообщение коммита`, `сгенерируй коммит` - агент собирается выполнить `git commit` или предложить текст коммита ## Шаг 0 — ОБЯЗАТЕЛЬНО (до любого текста коммита) **Первый вызов инструментов** в этой задаче — Shell из корня репозитория: ```powershell powershell -NoProfile -File scripts/commit/staged-context.ps1 ``` | Правило | Деталь | |---------|--------| | **MUST** | Запустить скрипт до генерации заголовка/тела коммита | | **MUST NOT** | Строить сообщение только по `git diff --cached` / `git status` без скрипта | | **MUST NOT** | Пропускать скрипт, даже если diff «и так понятен» | | Exit `1` | Index пуст — сообщить пользователю, **не коммитить** | | Exit `0` | Разобрать JSON stdout: `staged_count`, `groups[]` (`scope`, `files`, `stat`, `diff_excerpt`) | Дополнительно: [.cursor/rules/conventional-commits.mdc](../../rules/conventional-commits.mdc). ## Слияние групп (одна фича) Скрипт группирует **только по путям**. Перед split проверьте логическую связность: **Объединить в один коммит**, если это одна задача: - `docs/openapi.yaml` + `internal/httpapi/` (+ опционально `web/`) — один endpoint/контракт; - `migrations/` + `internal/store/` / `repository/` — одна схема; - правки теста рядом с кодом той же фичи. При объединении: один scope (доминирующий пакет, часто `httpapi` или `openapi`), один type, одно тело RU. **Auto-split** — если группы **не связаны** (например `web/` + `internal/birdfmt/` без общего смысла): **отдельный коммит на группу**, без вопроса пользователю. ## Порядок коммитов при split 1. `openapi` 2. `httpapi`, `store`, `jobs` 3. `pipeline`, `birdfmt`, `birddeploy`, `bundle` 4. `web` 5. `ci`, `deploy`, `db` 6. `docs` 7. `chore`, `cmd` ## Алгоритм auto-split (уровень файлов) 1. `git status` и `git diff --cached --stat` — зафиксировать полный список staged-файлов. 2. `git reset HEAD` — снять всё из index (working tree не трогать). 3. Для каждой (объединённой) группы по порядку выше: - `git add -- ` - Сгенерировать сообщение по правилу conventional-commits. - Закоммитить (см. ниже). 4. После всех коммитов — `git status` для проверки. **Ограничение:** частичный stage одного файла с разной семантикой — предупредить; split по hunk'ам не делать; предложить разнести правки по файлам. ## Создание коммита Только если пользователь **явно** просил закоммитить. Иначе — только вывести готовые сообщения. ```powershell git commit -m "$( @' (): <Тело на русском.> '@ )" ``` В PowerShell для многострочного тела используйте here-string как выше или `-m` для заголовка и `-m` для тела. Перед коммитом: `git status`, `git diff --cached` для группы — убедиться, что stage соответствует сообщению. ## Генерация текста По `groups[].diff_excerpt`, `stat`, `files`: ### Шаг A — semver (до выбора type) | Вопрос | Если «да» → | |--------|-------------| | Пользователь получает **новую** возможность? | `feat` (minor) | | Восстанавливается **ожидаемое** поведение / устранён баг? | `fix` (patch) | | Только скорость, контракт тот же? | `perf` (patch) | | Только структура кода/UI, поведение то же? | `refactor` (patch) | **Follow-up:** правки сразу после `feat` в том же scope без новой возможности → **`fix`**, не `feat` (слова *enhance/improve/refactor* в задаче не делают commit `feat`). **Запрещено** по умолчанию ставить `feat` для «большого diff» в `web/` — type по **semver impact**, не по объёму. ### Шаг B — type, scope, текст - **type** — результат шага A, не «chore по умолчанию» и не `feat` из-за слова enhance. - **scope** — из JSON группы или доминирующий при merge. - **summary** — конкретный, английский, императив; для `fix` — что **починено** (`fix broken …`, `prevent … loop`). - **body** — русский: что, зачем, edge cases, breaking impact. ## Вывод пользователю Для **каждого** коммита: ### 1. Готовое сообщение ``` (): <тело> ``` ### 2. Пояснение (RU) - **Semver impact:** `minor` | `patch` | `none` | `major` — и почему не другой type. - Риски и impact. - Split: сколько коммитов и почему. ## Не смешивать Один commit message — одна primary intent. Не объединять несвязанный `fix` и `feat` в один заголовок. ## Ссылки - Правило: `.cursor/rules/conventional-commits.mdc` - Скрипт: `scripts/commit/staged-context.ps1` - Просмотр групп: `powershell -File scripts/commit/staged-context.ps1 | ConvertFrom-Json`