Backend, Frontend, Weekend
В последнее время я активно использую Claude Code в своей разработке. По мере роста числа агентов я столкнулся с неожиданной проблемой — стало сложно синхронизировать их правила между разными системами.

Кроме того, появилось вполне рациональное опасение: в случае блокировки аккаунта можно потерять все наработки — команды, скилы и правила агентов, которые я собирал и оттачивал на протяжении нескольких месяцев. Это подтолкнуло меня к созданию удобной системы хранения конфигурации. Здесь важно зафиксировать простую мысль: директория ~/.claude/ — это такой же код. А код должен храниться в Git.
Так появился claude-config-template — репозиторий-шаблон, в котором живут скилы, агенты, команды, хуки и MCP-конфигурации. При этом сама папка ~/.claude/ собирается из него с помощью симлинков.
Если кратко, этот подход решает сразу три ключевые проблемы:
Дальше расскажу, как устроен шаблон и почему именно симлинки, а не копирование, оказались оптимальным решением.
Если вы хотя бы раз заглядывали в ~/.claude/, структура этого шаблона вас не удивит.
skills/ пользовательские скилы (SKILL.md + ресурсы)
agents/ субагенты (отдельные .md с frontmatter)
commands/ слеш-команды
mcp/ примеры конфигов MCP-серверов
hooks/ PreToolUse / PostToolUse и прочие хуки
docs/ конвенции, гайды, changelog
scripts/ утилиты: линтер, генератор новых скилов
linting/ pre-push-хук и набор проверок
И это сделано намеренно: чем меньше различий между репозиторием и реальной рабочей директорией, тем проще всё переносится и поддерживается.
Например, вы просто кладёте SKILL.md в skills/my-skill/ — и уже через минуту он доступен в Claude Code так, будто всегда там и находился.
Сам репозиторий по умолчанию пустой: никаких готовых скилов или агентов. Есть только agents/example.md — в качестве минимальной заглушки.
По сути, claude-config-template — это ваш персональный конфиг. Вы форкаете его и наполняете под свои задачи.
Самое интересное в этом шаблоне — не структура, а способ подключения.
Механика максимально простая:
make installСкрипт создаёт симлинки в ~/.claude/, указывающие на файлы из репозитория.
make install # создаёт ссылки в ~/.claude/
make uninstall # удаляет только наши ссылки, родные скилы не трогает
Важно: все ваши текущие файлы в ~/.claude/ остаются нетронутыми. make install ничего не копирует и не перезаписывает — он только создаёт ссылки.
Под капотом install.sh проходит по категориям (skills, agents, commands, hooks) и линкует каждый элемент отдельно.
ln -s "$REPO/skills/my-skill" "$CLAUDE_HOME/skills/my-skill"
ln -s "$REPO/agents/example.md" "$CLAUDE_HOME/agents/example.md"
Это принципиальный момент. Если бы мы линковали директории целиком (например, skills/), то затёрли бы встроенные скилы, которые Anthropic добавляет автоматически. Поэлементная линковка позволяет избежать этого: системные и пользовательские файлы спокойно сосуществуют в одной директории.
Что это даёт на практике:
SKILL.md в репозитории — Claude Code видит новую версию при следующем запуске. Не нужно копировать, переустанавливать, перезапускать что-то ещё.git blame по агенту, откатиться на старую версию, открыть PR на скил от коллеги.install.sh --uninstall удаляет ровно те ссылки, которые создал, и проверяет, что они до сих пор указывают на репозиторий. Чужие файлы в ~/.claude/ не трогает.Разумеется, у такого подхода есть и ограничения.
Если вы переместите репозиторий, симлинки «сломаются» — нужно будет снова запустить make install. Кроме того, если поверх симлинка вручную положить реальный файл, скрипт не станет его перезаписывать — он просто пропустит его.
Это осознанный компромисс: лучше ничего не трогать и избежать потери данных, чем случайно затереть чужую работу.
Пример вывода make install:
[skills]
+ /home/me/.claude/skills/my-skill -> /home/me/code/claude-config/skills/my-skill
= /home/me/.claude/skills/hello-test (already linked)
! /home/me/.claude/skills/manual-skill exists and is not a symlink. Skipping.
Когда скилы превращаются в обычный Git-репозиторий, перенос на новую машину становится тривиальной задачей:
git clone <repo>
cd <repo>
make install
И всё. Уже через несколько секунд в ~/.claude/ появляются симлинки на ваши скилы, агенты и команды.
Если что-то пошло не так, есть команда make doctor. Скрипт scripts/doctor.sh проверяет:
На выходе — понятный отчёт: что работает, а что нужно доустановить или поправить.
Скилы и агенты — это всего лишь Markdown с frontmatter. Казалось бы, что тут можно сломать.
На практике — очень многое. Достаточно:
И Claude Code просто молча проигнорирует часть файла — без ошибок и предупреждений.
Именно поэтому в шаблоне заложен довольно строгий (и местами параноидальный) набор проверок.
Они запускаются локально (через pre-push хук в linting/pre-push-check.sh) и в CI:
SKILL.md и agents/*.mdcommands/task_*.md по контрактуlinting/.markdownlint.yml).yamllint.yml).claude/settings*.jsonКаждая проверка оформлена как отдельный скрипт в linting/check_scripts/: check_shellcheck_all.sh, check_codespell_all.sh и так далее.
pre-push хук просто запускает их последовательно, а CI использует те же самые скрипты в виде отдельных джоб.
В результате — один источник правды для локальной разработки и для GitHub Actions.
Я не считаю этот шаблон серебряной пулей или обязательным стандартом. Это просто инструмент, который хорошо решает конкретный класс задач — но не все подряд.
Он действительно начинает приносить пользу, если:
В этих сценариях Git + симлинки дают предсказуемость: конфигурация всегда под рукой, изменения отслеживаются, а окружение легко восстановить.
С другой стороны, этот подход может быть избыточным.
Если у вас:
— то обычный файл в ~/.claude/skills/ решит задачу быстрее и проще.
Не стоит поднимать инфраструктуру ради инфраструктуры. Если всё помещается в один SKILL.md, Git-репозиторий и набор линтеров, скорее всего, только усложнят жизнь, а не упростят её.
В итоге получился не просто шаблон, а удобный способ навести порядок в своей работе с Claude Code: хранить конфигурацию как код, контролировать изменения и не зависеть от конкретной машины или аккаунта.
Если вам откликнулась эта идея — поддержите проект ⭐️ на GitHub. Это действительно помогает другим разработчикам находить его и экономить время.
Важно: этот шаблон задумывался не как готовое решение «из коробки», а как отправная точка. Форкните репозиторий, адаптируйте под себя и соберите свой собственный конфиг — такой, который идеально ложится в ваш процесс.
Если захотите поучаствовать в развитии — буду рад: багрепорты, идеи для новых проверок и pull request’ы с улучшениями только приветствуются. Процесс описан в CONTRIBUTING.md.
Newbie web-programmer
Backend, Frontend, Weekend
В прошлой статье я подробно разбирал линтинг кода: как подключить ESLint, как автоматизировать проверки через хуки, как сделать так, чтобы в репозиторий физически не попадал «грязный» код.
Но со временем я заметил странный перекос.
Мы очень строго относимся к исходникам, к JavaScript или TypeScript, к тестам, к типам. Там всё проверяется, форматируется, валидируется. А вот всё, что находится вокруг — HTML-шаблоны, Markdown-документация, YAML-конфиги — часто живёт по принципу «и так сойдёт».
Ни ESLint, ни тесты, ни типизация не скажут вам, что в YAML лишний пробел или в HTML забыли alt у картинки. Для них это всего лишь текст.
А для продакшена — потенциальный баг.
Когда я прихожу в новый проект и провожу аудит, почти всегда вижу одну и ту же картину. Код аккуратный, линтеры строгие, CI настроен. Но стоит открыть разметку или конфиги — и начинается творческий беспорядок. Кто-то форматирует по одному, кто-то по другому, кто-то копирует куски из StackOverflow, не особо понимая синтаксис.
Получается парадокс: мы защищаем самую очевидную часть системы и игнорируем инфраструктуру, документацию и шаблоны. Хотя по факту это такие же контракты проекта, просто записанные не на языке программирования, а на языках разметки.
Со временем я перестал разделять «код» и «не код». Если файл участвует в работе продукта — он должен быть проверяемым. Автоматически. Без надежды на внимательность разработчика.
Все представленные в этой записи скрипты, я разместил в своём репозитории:
Прежде чем углубляться в конкретные инструменты для HTML, Markdown и YAML, хочу поделиться своим подходом к применению линтеров в реальной работе.
В своей практике я использую два основных формата проверки файлов.
Первый — локальный, на уровне коммитов. Для этого у меня есть проверенные shell-скрипты, размещённые в репозитории. Они позволяют запускать линтеры только на тех файлах, которые были изменены и попали в git индекс. Это экономит время и ресурсы разработчика: линтер не тратит лишнее время на проверку всего проекта, а концентрируется только на новых или изменённых файлах.
Пример запуска локальной проверки через pre-commit выглядит так:
#!/bin/bash
set -e
ALL_FILE_ARRAY=()
while IFS= read -r line; do
ALL_FILE_ARRAY+=("$line")
done < <(git diff --cached --name-only --diff-filter=ACM || true)
echo "Markdown code style checker..."
bash scripts/check_scripts/check_markdownlint.sh "${ALL_FILE_ARRAY[@]}"
echo "----------"
Эти скрипты интегрированы через git-хуки, обычно pre-commit или pre-push. Хук получает список изменённых файлов, передаёт их в скрипт, и линтер проверяет именно эти файлы. Если находятся ошибки, коммит или пуш блокируется, и разработчик сразу получает обратную связь.
Второй формат проверки — полная проверка проекта в CI. Здесь линтеры анализируют все файлы, независимо от того, что изменилось. Это полезно для выявления старых ошибок, которые могли «просочиться» раньше, или для полной регрессионной проверки после обновления конфигурации линтеров. В моём репозитории такие скрипты имеют суффикс _all, чтобы отличать их от версий, работающих с отдельными файлами.

Использование комбинации локальных проверок через хуки и полной проверки в CI — это то, что я называю двухуровневой стратегией линтинга. Локально линтер экономит время разработчика и ловит ошибки сразу, а CI гарантирует, что весь проект остаётся консистентным, даже если что-то пропустили на этапе коммита.
CSS опирается на структуру DOM. JavaScript опирается на селекторы и атрибуты. Поисковики — на валидность и иерархию.
И при этом именно HTML чаще всего пишут в режиме «да браузер всё равно проглотит».
Браузеры действительно очень терпеливые. Они автоматически закрывают теги, чинят вложенность, додумывают структуру. Но они не обязаны работать так, как вы ожидаете.
Когда вы полагаетесь только на рендеринг, вы перекладываете ответственность на поведение движка. Но движков несколько, и у каждого свои особенности. То, что Chrome аккуратно «починил», в Firefox или Safari может дать совершенно другую структуру DOM.
Плюс есть вещи, которые браузер вообще не обязан проверять. Например, наличие alt у изображений или корректную семантику заголовков. Для него это валидно. Для пользователя — нет.
С точки зрения доступности и поддержки продукта такие «мелочи» стоят очень дорого. Особенно когда проект растёт и шаблонов становится сотни.
В своих проектах я ставлю HTML-линтер буквально одним из первых инструментов. Сейчас это HTMLHint.
Он делает ровно то, что должен делать хороший линтер: спокойно и без лишнего шума находит структурные проблемы, которые человек легко пропускает глазами.
Речь не только про банальные незакрытые теги. Линтер проверяет корректность атрибутов, следит за семантикой, предупреждает о странной вложенности элементов, помогает не забывать обязательные вещи вроде alt или уникальных id. По сути, это автоматический «придирчивый ревьюер», который никогда не устает.
Особенно хорошо он ловит кроссбраузерные грабли — некорректный nesting, дублирование атрибутов, потенциально опасные конструкции, которые потом превращаются в трудноуловимые визуальные баги.
Я не сторонник агрессивных правил ради правил. Конфиг должен решать реальные проблемы, а не мешать работе. Поэтому обычно начинаю с довольно приземлённого набора.
Вот пример конфига .htmlhintrc:
{
"tagname-lowercase": true,
"attr-lowercase": true,
"attr-value-double-quotes": true,
"attr-no-duplication": true,
"doctype-first": false,
"empty-tag-not-self-closed": true,
"id-unique": true,
"src-not-empty": true,
"title-require": false,
"alt-require": true,
"id-class-value": false,
"style-disabled": false,
"inline-style-disabled": false,
"head-script-disabled": false,
"img-alt-require": true,
"attr-unsafe-chars": true
}
Плюсы конфигурации:
tagname-lowercase: true Требует писать имена тегов в нижнем регистре (div, img, section).
attr-lowercase: true Атрибуты только в нижнем регистре (class, href, src).
attr-value-double-quotes: true Значения атрибутов только в двойных кавычках.
attr-no-duplication: true Запрещает дублирование атрибутов (class=”a” class=”b”).
doctype-first: false Не требует <!DOCTYPE html> в начале файла. Удобно для partials, layout’ов и шаблонов, где doctype генерируется отдельно.
empty-tag-not-self-closed: true Требует корректный синтаксис для пустых (void) тегов (img, br, hr)..
id-unique: true Каждый id должен быть уникальным.
src-not-empty: true Запрещает пустой src у img/script/iframe.
title-require: false Не требует title в каждом файле. Подходит для шаблонных систем.
alt-require: true Требует alt у изображений.
id-class-value: false Не навязывает стиль имен (kebab-case, BEM и т.д.).
style-disabled: false Разрешает тег style. Практично для контентных страниц.
inline-style-disabled: false Разрешает inline style. Для больших приложений нежелательно, но для блога нормально.
head-script-disabled: false Разрешает script в head.
img-alt-require: true Дополнительно проверяет наличие alt именно у img.
attr-unsafe-chars: true Запрещает небезопасные символы в атрибутах. Защищает от кривой разметки и потенциальных XSS/парсинг-ошибок.
Вот пример скрипта для проверки HTML файлов всего проекта:
#!/bin/bash
# ----------------------------------------
# HTML Code Checker (Full Project)
#
# This script checks all HTML files in the
# project for issues using HTMLHint.
# Used in pre-push-check.sh
#
# Usage:
# ./check_htmlhint_all.sh
# ----------------------------------------
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
HTML_FILES=$(find "$PROJECT_DIR" -name "*.html" -not -path "*/_site/*" -not -path "*/node_modules/*" -not -path "*/.git/*")
if [ -z "$HTML_FILES" ]; then
echo "No HTML files found"
exit 0
fi
exec "$SCRIPT_DIR/check_htmlhint.sh" $HTML_FILES
На практике этого достаточно, чтобы заметно снизить количество мелких багов в шаблонах.
Когда речь заходит о документации, многие думают: «Markdown — это просто текст, ошибок здесь быть не может». На практике это заблуждение.
Markdown — это не просто README. Это спецификации, инструкции для коллег, шаблоны PR, чеклисты и даже часть автоматических changelog‑ов. И если в этих файлах допустить опечатку или сбой форматирования, последствия будут заметны: списки съезжают, заголовки теряют иерархию, ссылки становятся битые, изображения не отображаются, и автоматические сборки changelog‑ов ломаются.
Markdownlint помогает автоматизировать проверку, ловя сразу несколько классов проблем. Он следит за последовательностью заголовков, соблюдением отступов и пробелов вокруг элементов, корректным оформлением списков и длиной строк. Линтер проверяет ссылки и изображения, что критично для документации, которая активно используется в CI/CD пайплайнах или генерирует автоматические отчёты.
Для меня Markdownlint стал неотъемлемой частью работы с текстом, особенно когда речь идёт о блоге. Я использую Jekyll в качестве движка, и весь контент хранится в Markdown‑файлах.
В своём блоге я использую следующую конфигурацию .markdownlint.yml:
# Markdownlint configuration for blog project
# Disable line length — blog posts have long prose lines
MD013: false
# Disable blanks around lists — Jekyll content often has lists right after text
MD032: false
# Disable unordered list style — allow both - and *
MD004: false
# Disable list marker space — allow flexible spacing
MD030: false
# Disable trailing spaces — not critical for blog
MD009: false
# Disable fenced code language — not all code blocks need a language
MD040: false
# Disable emphasis as heading — used stylistically in posts
MD036: false
# Disable bare URLs — acceptable in blog content
MD034: false
# Disable multiple blank lines — used for visual separation
MD012: false
# Disable blanks around headings — Jekyll layout constraints
MD022: false
# Disable ordered list prefix style — allow 1. 2. 3.
MD029: false
# Disable blanks around fences — layout constraints
MD031: false
# Disable heading increment — blog posts may skip heading levels
MD001: false
# Disable table column style — flexible table formatting
MD060: false
# Require single trailing newline
MD047: true
Плюсы конфигурации:
Гибкость в длине строк (MD013: false) Отлично для блогов с длинными абзацами. Не придётся ломать строки искусственно.
Более свободная работа со списками (MD004, MD030, MD029, MD032) Позволяет использовать разные маркеры (- или *) и разные стили нумерации.
Стиль и визуальное оформление (MD036, MD034, MD012, MD022, MD031) Разрешены пропуски между блоками, пропуски вокруг заголовков и визуальные разделители. Это удобно для Jekyll-шаблонов и при использовании Markdown как исходника для HTML с кастомным CSS.
Техническая гибкость (MD009, MD040, MD047) Игнорируются trailing spaces и отсутствие указания языка в fenced code blocks. Требуется только один trailing newline (MD047), что важно для корректной компиляции Markdown в HTML.
Вот пример скрипта для проверки Markdown файлов всего проекта:
#!/bin/bash
# ----------------------------------------
# Markdown Code Style Checker (Full Project)
#
# This script checks all Markdown files in the
# project for style issues using markdownlint.
# Used in pre-push-check.sh
#
# Usage:
# ./check_markdownlint_all.sh
# ----------------------------------------
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
MD_FILES=$(find "$PROJECT_DIR" -name "*.md" -not -path "*/_site/*" -not -path "*/node_modules/*" -not -path "*/.git/*")
if [ -z "$MD_FILES" ]; then
echo "No Markdown files found"
exit 0
fi
exec "$SCRIPT_DIR/check_markdownlint.sh" $MD_FILES
Markdownlint стал для меня таким же естественным инструментом, как ESLint для кода: не для красоты, а для надёжности и предсказуемости проекта.
Если HTML и Markdown иногда «прощают» мелкие ошибки, то YAML почти никогда.
Это один из самых популярных форматов в DevOps:
Почти вся современная инфраструктура описывается именно в YAML.
Проблема в том, что YAML очень чувствителен к отступам, спецсимволам и контексту. Один лишний или недостающий пробел способен превратить рабочий пайплайн в silent‑fail: процесс просто не запускается, а причина в логах бывает неочевидной.
На практике я решаю эту проблему с помощью yamllint. Он не только ловит синтаксические ошибки, но и проверяет структуру файлов, соблюдение отступов, корректность типов и повторяющихся ключей. По сути, это автоматический сторож, который гарантирует, что конфиги остаются читаемыми и работоспособными.
Особенно полезно интегрировать yamllint прямо в CI/CD пайплайн. На этапе сборки или тестирования он проверяет все YAML‑файлы, и если есть нарушения — сборка падает ещё до деплоя.
Вот пример конфигурации .yamllint.yml:
---
extends: default
rules:
line-length:
max: 200
truthy:
check-keys: false
comments:
min-spaces-from-content: 1
document-start: disable
braces:
max-spaces-inside: 1
indentation:
spaces: 2
indent-sequences: true
Плюсы конфигурации:
line-length: max 200 Значение 200 — очень гибкое. Позволяет писать длинные строки, например, длинные ссылки, сложные тексты или JSON в YAML. Для блог-постов или конфигураций Jekyll/Hugo удобно.
truthy: check-keys: false Отключает проверку ключей на “truthy” (yes, no, on, off). Удобно, если ключи используются в строковом виде (“yes”, “no”), а не как булевы значения.
comments: min-spaces-from-content: 1 Требует хотя бы 1 пробел после #. Хорошая практика, делает комментарии читаемыми.
document-start: disable Не обязательно ставить — в начале файла. Часто нужно для небольших YAML-конфигов или фронтматтеров Jekyll.
braces: max-spaces-inside: 1 Контролирует пробелы внутри {} и []. Например, { key: value } с 1 пробелом — аккуратно, но не строгая фиксация. Полезно, если в проекте есть inline объекты или массивы.
indentation: spaces: 2 Классические 2 пробела, что стандартно для большинства проектов.
indent-sequences: true значит, что элементы списков должны быть на том же уровне, что и их родительский блок, а не на один уровень меньше.
Использование yamllint превращает YAML из потенциального источника скрытых багов в предсказуемый и надёжный инструмент управления инфраструктурой. И чем раньше его подключить, тем меньше «маленьких» ошибок потом вырастают в большие проблемы.
Благодаря линтерам ревьюеры могут концентрироваться на том, что действительно важно: архитектуре, дизайну интерфейса, логике и решениях, которые влияют на продукт. Они больше не тратят время на исправление лишних пробелов, неправильных отступов или мелких синтаксических ошибок в YAML, Markdown или HTML.
Это не просто про порядок и чистоту кода. Это про культуру качества, которая встроена в процесс разработки автоматически. Каждый коммит, каждый пуш, каждая документация проверяется с одинаковой строгостью — без усталости и человеческого фактора.
В итоге линтеры превращают хаос в предсказуемость, случайности — в стабильность, а рутинные ошибки — в уверенность. И это именно то, что делает проекты масштабируемыми и команды эффективными.
{message}