Как научить AI писать коммиты по правилам вашего проекта, а не Conventional Commits по умолчанию

от автора

Любой AI-инструмент умеет генерировать commit message. Проблема в том, что он генерирует что-то разумное — но не то, что принято в вашем проекте: не знает ваш формат с тикетами, не вытаскивает номер задачи из ветки, не учитывает какие типы у вас разрешены.

В этой статье я покажу как один раз описать правила своего проекта так, чтобы AI следовал им предсказуемо — каждый раз. Основной пример на Claude Code, но паттерн и готовый скрипт переносятся на любой инструмент: Cursor, Copilot Chat, git hook с API-вызовом.


Проблема, которую не решает commitlint

Commitlint — отличный инструмент. Он ловит коммиты неправильного формата и не даст смержить ветку с wip или асдф в истории. Но он не помогает их написать.

Он говорит «неправильно» — и возвращает тебя к пустой строке. Сформулировать правильное сообщение всё равно надо самому.

В конце рабочего дня, когда ты только что пофиксил хитрый баг в маппере — это отдельная мыслительная задача после нескольких часов другой мыслительной работы. Поэтому даже в проектах с commitlint история нередко выглядит так:

git log --onelinea3f1c2e fix: fixed9bd04a1 feat: wipc782d3f refactor: changes4e910bb fix: bug2a33f0c chore: update

Формат формально соблюдён. Commitlint доволен. История бесполезна.

Вместо того что должно быть:

git log --onelinea3f1c2e DEV-1677/feat(discrepancies): реализовать полнотекстовый поиск ТОРГ-29bd04a1 DEV-1676/feat(auth): добавить JWT-аутентификацию с mock-режимом для разработкиc782d3f DEV-1704/fix(delivery): исправить фильтрацию поставок по статусу4e910bb DEV-1698/refactor(etrn): упростить маппер статусов электронной накладной2a33f0c DEV-1690/test(acceptance): добавить интеграционные тесты приёмки

Как я к этому пришёл

Проблема не новая, решений много. Я прошёл через несколько:

commitizen — первое что попробовал. Интерактивный wizard в консоли задаёт вопросы: тип? scope? описание? Звучит удобно, но на практике замедляет: ты и так знаешь что хочешь написать, просто не хочешь думать о синтаксисе. Плюс под наш нестандартный формат с тикетом в префиксе он не гнётся без плясок с конфигом.

Git hook + локальная LLM — видел статью на Хабре, попробовал. Работает, но требует настройки на каждой машине и зависит от того, какая модель стоит локально. При смене ноутбука надо всё поднимать заново.

IDE плагины (JetBrains AI, GitLens) — кнопка «Generate commit message» есть. Жмёшь — получаешь что-то вроде feat: update delivery service. Формат проекта не знает, тикет не вытаскивает, каждый раз дописываешь руками.

Готовые скилы из Claude Directory — когда начал плотно работать с Claude Code, нашёл готовый /commit в маркетплейсе. Тот же результат: Conventional Commits по умолчанию, без понятия о нашем формате с тикетами.

Просто спрашивать Claude — «напиши коммит для этих изменений» работает, но непредсказуемо: иногда одна строка, иногда три абзаца с объяснениями, иногда в markdown-блоке. Нельзя просто скопировать.

У всех этих решений одна общая проблема: они не знают о правилах конкретного проекта. Генерируют что-то разумное — но не то, что ожидает ваш commitlint и ваши коллеги. Решение — один раз явно описать правила своего проекта и получать предсказуемый результат каждый раз.


Соглашения по коммитам

Чтобы было понятно о чём речь — вот формат с которым я работаю:

<ticket>/<type>(<scope>): <description>
  • <ticket> — номер задачи из трекера: DEV-123PROJ-42

  • <type> — featfixrefactortestcidocsstyleperfbuildhotfix

  • <scope> — модуль или слой: deliveryauthgoods

  • <description> — повелительное наклонение, строчная буква, без точки, на русском

Правила зафиксированы в .git-commit-template — он открывается в редакторе при ручном git commit. Этот же алгоритм мы реализуем для AI-assisted пути.

Формат несложный, но каждый раз требует: вспомнить номер задачи из имени ветки, выбрать правильный тип, сформулировать повелительное наклонение, не забыть про строчную букву. Это ~10 секунд думать — каждый коммит, каждый день.


Что такое скилы в Claude Code

Claude Code — AI-ассистент в CLI, который работает прямо в терминале. Скилы — это его расширение: markdown-файл с инструкциями, который живёт в ~/.claude/skills/<name>/SKILL.md и вызывается командой /name.

Это не плагин и не скрипт. Это структурированный prompt с жёстким алгоритмом — предсказуемый, версионируемый, командный.

Ключевое отличие от «просто спросить модель»: скил каждый раз выполняет одни и те же шаги в одном и том же порядке и выдаёт результат в одном и том же формате. Это воспроизводимо.

Почему не просто CLAUDE.md?

Резонный вопрос: а зачем отдельный скил, если можно добавить правила коммитов прямо в CLAUDE.md?

Можно. Но разница в том, как работает каждый из инструментов.

CLAUDE.md — это фоновый контекст: файл загружается в каждый разговор и описывает проект в целом — архитектуру, соглашения, стек. Это инструкции «как думать об этом коде». Если туда добавить пошаговый алгоритм генерации коммита, он будет лежать мёртвым грузом в каждом запросе — когда вы просите объяснить функцию, исправить баг, написать тест.

Скил — это явный вызов/commit-msg говорит модели «сейчас делаем только это, по этому алгоритму». Нет борьбы за внимание с архитектурными правилами и описанием стека. Алгоритм выполняется полностью, в нужном порядке, без оговорок.

Ещё один аргумент: скил — единица распространения. Его можно поставить из маркетплейса, передать коллеге одним файлом, опубликовать с версией. CLAUDE.md — это файл проекта, он не путешествует отдельно.

Практическое правило: CLAUDE.md описывает что есть в проекте, скил описывает что сделать прямо сейчас.


Демо

Шаг 1 — смотрим что изменилось:

$ git statusOn branch feature/DEV-2041-delivery-paginationChanges not staged for commit:  modified: src/digital_delivery/infrastructures/db/repositories/delivery.py  modified: src/digital_delivery/application/use_cases/list_deliveries.py  modified: src/digital_delivery/presentation/api/rest/v1/controllers/delivery_controller.py  modified: src/digital_delivery/presentation/api/rest/v1/schemas/delivery.py

Четыре файла, три слоя архитектуры. Формулировать руками — надо держать в голове что именно менялось.

Шаг 2 — запускаем скил:

> /commit-msg

Агент последовательно читает контекст — это видно в интерфейсе Claude Code:

● bash: git branch --show-current● bash: git diff HEAD

Читает реальный код, а не только имена файлов — поэтому понимает смысл изменения, а не просто перечисляет что поменялось.

Шаг 3 — результат:

DEV-2041/feat(delivery): добавить пагинацию в список поставок

Одна строка. Копируешь, вставляешь:

git commit -m "DEV-2041/feat(delivery): добавить пагинацию в список поставок"

Полный исходник скила

Файл ~/.claude/skills/commit-msg/SKILL.md:

---name: commit-msgdescription: >  Генерирует готовое git commit message по изменениям текущей ветки  в формате проектного шаблона: <ticket>/<type>(<scope>): <description>.  Использовать когда: "напиши commit message", "придумай коммит",  "suggest a commit", /commit-msg.---# commit-msgСгенерировать точное commit message по формату проекта.## Шаги1. Получить контекст изменений:   git branch --show-current   git diff HEAD   git diff --cached   Если оба diff пусты — посмотреть на untracked файлы в git status.2. Определить ticket из имени ветки: паттерн [A-Z]+-\d+   - feature/DD-123-add-pagination → DD-123   - fix/PROJ-7-broken-mapper → PROJ-7   - Если не найден — писать без префикса: <type>(<scope>): <description>3. Проанализировать изменения: что добавлено, удалено, изменено   и каков смысл изменения, а не только какие строки поменялись.4. Выбрать тип:   feat     — новый функционал   fix      — исправление ошибки   refactor — рефакторинг без изменения поведения   test     — тесты   ci       — CI/CD, скрипты, пайплайны   docs     — документация   style    — форматирование, codestyle   perf     — оптимизация производительности   build / chore — сборка, зависимости, конфиги   hotfix / revert — критические исправления или откат5. Составить описание:   - Повелительное наклонение: «добавить», не «добавлено»   - Строчная первая буква, без точки в конце   - На русском языке   - Не более ~72 символов включая весь префикс6. Вывести ТОЛЬКО результат — без объяснений, без markdown-блоков,   без «вот твоё сообщение:». Просто текст для копирования.   - Одно изменение: одна строка commit message.   - Два или более несвязанных изменения: перечислить найденные изменения,     предложить разбить на отдельные коммиты и для каждого выдать     готовое сообщение.

Ещё два случая из практики

Уточнение формулировки

Первый вариант не нравится — слишком общий:

> перефразируй, акцент на том что mock только для dev-окруженияDEV-1676/feat(auth): добавить JWT с автоматическим mock-режимом вне production

Это диалог, не одноразовый prompt. Можно итерировать.

Грязный diff — самый ценный сценарий

Ты работал над фичей и по пути исправил несвязанный баг. В diff — два разных изменения. Сам бы ты, скорее всего, написал один коммит и не заметил.

Агент видит diff целиком и реагирует иначе:

> /commit-msgВ diff обнаружено два несвязанных изменения.Рекомендую разбить на два коммита — добавьте файлы каждого изменения отдельно через git add.1. DEV-1677/feat(discrepancies): реализовать полнотекстовый поиск ТОРГ-22. DEV-1677/fix(delivery): исправить обработку пустого списка позиций

Адаптация под свой проект за 5 минут

Скил — текстовый файл. Правила проекта прописываются в SKILL.md явно — всегда понятно чему именно он следует, менять легко.

Ниже примеры того, что вписать в секцию ## Шаги вместо наших правил — под разные командные форматы:

Conventional Commits, английский:

Формат: <type>(<scope>): <description>Язык: английский, imperative mood ("add", "fix", "remove")Тикет: не используется

Jira в скобках:

Формат: [PROJ-123] <type>(<scope>): <description>Тикет: в квадратных скобках в начале, из имени ветки

Автоопределение языка по истории:

Посмотреть последние 5 коммитов через git log --oneline.Если коммиты на русском — описание на русском.Если на английском — на английском.

Принудительный тип по ветке:

Если ветка начинается с hotfix/ — тип всегда hotfix, игнорировать остальные правила выбора типа.

Правила прописываются один раз — если конвенции команды изменились, обновляешь SKILL.md тем же PR.


Идея работает без Claude Code

SKILL.md — это обычный текстовый файл. Внутри — пронумерованный алгоритм на человеческом языке. Ничего специфичного для Claude Code в нём нет.

Если вы работаете в другом инструменте, тот же подход переносится напрямую:

Cursor / Copilot Chat / любой чат с LLM — скопируйте содержимое секции ## Шаги как системный промпт или вставьте в начало запроса. Модель выполнит те же шаги.

Git hook + API — если хотите автоматику при каждом git commit, оберните алгоритм в commit-msg hook с curl-вызовом к любому LLM API:

#!/bin/shDIFF=$(git diff --cached)BRANCH=$(git branch --show-current)SKILL=$(cat "$(git rev-parse --show-toplevel)/.claude/skills/commit-msg/SKILL.md")COMMIT_MSG=$(jq -n \  --arg skill "$SKILL" \  --arg branch "$BRANCH" \  --arg diff "$DIFF" \  '{model:"gpt-4o",messages:[    {role:"system",content:$skill},    {role:"user",content:"Branch: \($branch)\n\nDiff:\n\($diff)"}  ]}' \  | curl -s https://api.openai.com/v1/chat/completions \    -H "Authorization: Bearer $OPENAI_API_KEY" \    -H "Content-Type: application/json" \    -d @- \  | jq -r '.choices[0].message.content')echo "$COMMIT_MSG" > "$1"

Положите скрипт в .git/hooks/commit-msg и сделайте исполняемым:

chmod +x .git/hooks/commit-msg

При следующем git commit hook запустится автоматически и запишет сгенерированное сообщение.

Локальная модель (Ollama) — та же схема, только URL http://localhost:11434.

Скил в ~/.claude/skills/ — удобная форма упаковки для Claude Code. Но суть переносима: один раз описать правила проекта как чеклист, получать предсказуемый результат в любом инструменте.


Командное использование: скил как часть репозитория

Скил можно положить прямо в репо:

.claude/  skills/    commit-msg/      SKILL.md

Тогда:

  • Все разработчики получают одинаковое поведение без настройки

  • Изменение формата коммитов = PR в SKILL.md, обсуждается как любой код

  • Новый человек в команде сразу пишет коммиты правильно

Это меняет природу инструмента: из личного помощника он становится частью командных конвенций.


Побочный эффект: дисциплина именования веток

Скил вытаскивает номер тикета из имени ветки. Если ветка называется fix-bug или my-feature — тикета нет, скил пишет коммит без префикса.

Это создаёт мягкое давление называть ветки правильно:

feature/DEV-123-add-search   → DEV-123/feat(search): ...  ✓fix/PROJ-7-broken-mapper     → PROJ-7/fix(mapper): ...    ✓my-branch                    → feat(<scope>): ...           (тикет потерян)

Никакого линтера на имена веток не нужно. Разработчик сам быстро замечает связь: назвал ветку правильно — скил работает полностью, назвал абы как — тикет в коммите пропал, придётся дописывать руками. Инструмент воспитывает привычку, не требуя её принудительно.


Ограничения

  • Не заменяет commitlint на CI. Скил помогает написать правильно, но не гарантирует это. Линтер всё равно нужен как страховка.

  • Большой грязный diff. На 500+ строках из несвязанных файлов формулировка может получиться общей. Лучше коммитить небольшими порциями.

  • Требует Claude Code в базовом варианте. Альтернативы — см. раздел «Идея работает без Claude Code».

  • Нет автозапуска в варианте с Claude Code. Скил вызывается вручную; для автоматики при каждом git commit используйте git hook — пример есть в разделе «Идея работает без Claude Code».


Итог

Коммит-сообщение — маленькая задача, которая повторяется сотни раз в год. Каждый раз несколько секунд на то, чтобы вспомнить формат, извлечь номер тикета из имени ветки, сформулировать повелительное наклонение. Скил убирает эту нагрузку и делает это воспроизводимо.

Главная идея не в том, что «Claude Code умеет генерировать коммиты». Главная идея — один раз описать правила своего проекта как пронумерованный чеклист и получать предсказуемый результат: в Claude Code через /commit-msg, в Cursor через системный промпт, в любом CI через git hook с API-вызовом.

ссылка на оригинал статьи https://habr.com/ru/articles/1044636/