Наткнулся на github.com/DietrichGebert/ponytail — плагин для Claude Code, который превращает агента в ленивого сеньора-максималиста. Не в смысле "напишет плохо", а в смысле "прежде чем написать хоть строчку, спросит — а это вообще должно существовать?".
Идея зацепила сразу. Большая часть технического долга появляется не потому, что кто-то плохо кодит, а потому, что никто не задаёт этот вопрос вовремя. Библиотека ставится "на всякий случай". Абстракция пишется "чтобы было гибко". Валидатор добавляется один раз и потом никто больше на него не смотрит — просто верят зелёной галочке.
Установил, погонял на своём Next.js лендинге. Результат — минус 2985 строк кода за пять раундов и один настоящий баг, который жил в проекте больше года и о котором я не подозревал.
Что это такое и как установить
Ponytail — не отдельная программа, а плагин-маркетплейс для Claude Code. Он добавляет один персистентный режим и пять one-shot команд поверх него.
Установка — три строчки прямо в диалоге с Claude Code:
/plugin marketplace add DietrichGebert/ponytail
/plugin install ponytail@ponytail
/reload-plugins
После перезагрузки плагинов становятся доступны все команды ниже. Никакого конфига писать не нужно — по умолчанию активен режим full.
Гайд: что умеет каждая команда
/ponytail — постоянный режим для кодинга
Это не разовая команда, а переключатель поведения агента на всю сессию. Включив его, вы меняете сам процесс написания кода: агент проходит через "лестницу" вопросов, прежде чем писать что-либо — нужна ли эта штука вообще, есть ли уже готовое решение в кодовой базе, закрывает ли это стандартная библиотека, хватит ли одной строчки. Только если все пункты отвечены "нет", он пишет полноценный код.
Есть три уровня интенсивности:
/ponytail lite — построит что просили, но в одну строчку упомянет более ленивую альтернативу
/ponytail — режим по умолчанию, вся лестница вопросов обязательна
/ponytail ultra — YAGNI-максимализм, оспаривает само требование в том же ответе
Пример разницы на задаче "добавь кэш для этих API-ответов":
- lite: сделает кэш-класс, но добавит — "кстати,
functools.lru_cacheрешает это в одну строку". - full: сразу поставит
@lru_cache(maxsize=1000), ничего лишнего не напишет. - ultra: откажется добавлять кэш вообще, пока профайлер не покажет, что он нужен.
Отключается фразой "stop ponytail" или "normal mode", включается заново командой /ponytail.
/ponytail-review — ревью текущего diff
Смотрит только на изменения, которые вы ещё не закоммитили, и ищет исключительно переусложнение — не баги, не безопасность, только избыточную сложность. Формат вывода — одна строка на находку:
L42: yagni: factory на один продукт. Инлайн.
L88: stdlib: свой email-валидатор на 30 строк. re.match с одним regex.
Ничего не правит, только показывает список. Хорош перед коммитом, когда хочется быстро проверить — не притащил ли я лишнюю абстракцию попутно с фичей.
/ponytail-audit — аудит всего репозитория
Это то, что я использовал для своего лендинга. В отличие от review, здесь сканируется не diff, а весь проект целиком: неиспользуемые зависимости, мёртвые файлы, дублирующийся код, конфиги без единого читателя. Тоже только список, ничего не применяет автоматически — решение удалять или нет остаётся за вами.
/ponytail-debt — реестр отложенных решений
Когда агент в режиме full/ultra сознательно срезает угол — например, ставит глобальную блокировку вместо блокировки по-аккаунтно, — он обязан оставить комментарий вида:
# ponytail: global lock, per-account locks if throughput mattersКоманда /ponytail-debt собирает все такие пометки по всему проекту в один список. Это честный технический долг: не "забыли", а "сознательно отложили, вот где искать, если понадобится".
/ponytail-gain — сводка измеренного эффекта
Показывает усреднённые цифры с бенчмарков авторов плагина (не про ваш конкретный проект) — сколько в среднем ponytail сокращает код, стоимость и время на типовых задачах. Полезно, чтобы понять порядок величин, прежде чем гонять аудит на своём коде.
/ponytail-help — шпаргалка
Просто выводит эту же таблицу команд прямо в чате, если забыли, что где вызывается.
Аудит моего проекта: пять раундов
Дальше — то, что происходило у меня, когда я применил /ponytail-audit к своему лендингу на 79z.ru.
Раунд 1: 43 зависимости, которых там не должно было быть
Первым делом агент открыл package.json и увидел 26 пакетов @radix-ui/react-* — accordion, dialog, dropdown-menu, tooltip, весь набор.
Мой сайт — это терминал в стиле Pip-Boy из Fallout. Зелёный текст на чёрном фоне, никаких модалок, никаких тултипов. Представьте, что вы держите склад с полками под сорок разных инструментов, а реально пользуетесь только молотком. Остальное просто занимает место и создаёт риск, что кто-то однажды об него споткнётся.
Ponytail прогнал grep по каждому пакету отдельно, чтобы не гадать, а получить твёрдое доказательство:
for pkg in accordion alert-dialog dialog select tabs tooltip ...; do
grep -rl "@radix-ui/react-$pkg" --include="*.tsx" . | grep -v node_modules
doneНоль совпадений. По всем 26. Плюс ещё 15 сверху — recharts, zod, react-hook-form, date-fns, sonner. Это был скелет UI-конструктора от v0.dev-скаффолда, которым я так и не начал пользоваться. Даже конфиг components.json указывал на папку @/components/ui, которой физически не существовало на диске.
41 зависимость и мёртвый конфиг ушли одним коммитом. Первая победа далась легко — слишком легко, как позже выяснилось.
Раунд 2: галочка, которая врала
На втором проходе я попросил ponytail не искать зависимости, а посмотреть на весь код репозитория — восемь параллельных под-агентов, каждый со своим углом зрения: корректность логики, повторное использование, эффективность, соответствие внутренним правилам проекта.
Один из них зацепился за три кастомных валидатора, встроенных прямо в сборку сайта — проверку канонических ссылок, безопасность HTML в блоге, стилевую политику. Тут и начинается настоящая драма.
Мой блог когда-то переехал с формата index.html на index.mdx. Валидаторы — нет. Вот что происходило внутри:
const htmlPath = join(blogPostsDir, dir.name, "index.html")
try {
readFileSync(htmlPath, "utf-8")
validateHtmlFile(htmlPath)
} catch {
// File doesn't exist or can't be read, skip
}Представьте охранника на входе, которому поручили проверять сумки посетителей. Три последних посетителя вошли через боковую дверь, которую охранник не знает. Вместо того чтобы поднять тревогу — он просто не заметил, что кто-то вошёл, и записал в журнал "всё чисто".
Я не поверил находке на слово и запустил проверку вручную. Терминал вывел уверенную зелёную строку: "All blog posts passed security validation". Красиво, убедительно — и абсолютно ложно для трети моего контента. Три последних статьи блога вообще не сканировались на встроенный исполняемый код и обработчики событий в разметке. Проверка тихо решила, что их не существует.
Разница между "проверка прошла" и "проверка что-то реально проверила" — не мелочь придирчивого перфекциониста. Молчаливый catch-блок — это не изящная деградация, это дыра, которая наряжается в костюм успеха и обманывает именно того, кто должен был это заметить.
Как это чинили: обе стороны разрыва, а не одну
Соблазн был очевидный — просто научить валидатор смотреть и на .mdx, оставив старую логику как есть. Но проверка дерева показала: семь из десяти папок с постами тащили мёртвый index.html рядом с рабочим .mdx — мусор от старой миграции, который никто не убрал.
Удалил все семь мёртвых файлов. Переписал оба валидатора так, чтобы они читали содержимое настоящего источника — frontmatter через gray-matter — вместо поиска HTML-тегов в файле, которого больше нет:
function extractUrlsFromMdxFrontmatter(content: string): string[] {
const { data } = matter(content)
const urls: string[] = []
if (typeof data.canonical === "string") urls.push(data.canonical)
if (typeof data.ogImage === "string") urls.push(data.ogImage)
return urls
}Прогнал ещё раз. Зелёные галочки появились для всех десяти постов — и на этот раз они были настоящими. Заодно всплыл третий валидатор, который вообще никогда не мог провалить сборку: при найденном расхождении стилей он печатал предупреждение и всё равно завершался успешно. Проверка, которая физически не умеет сказать "нет", — это не проверка, это театр с бутафорским замком на двери.
Раунды 3–5: чем глубже копаешь, тем больше находишь
После находки с валидатором я не остановился на одном проходе. Каждый следующий запуск /ponytail-audit находил что-то новое — не потому что код портился на глазах, а потому что первый проход искал явно мёртвые зависимости, а не связи между файлами.
Третий раунд вскрыл дублирование в хелпере блога: две функции параллельно читали один и тот же файл поста, с разным кастингом типов полей. Работали одинаково — пока что. Но при добавлении нового поля в будущем пришлось бы редактировать обе копии руками, и ничего не гарантировало, что я не забуду одну из них. Свёл к одной внутренней функции, поверх которой — два тонких публичных вызова. Скучно, и поэтому надёжно.
Четвёртый раунд нашёл зависимость rehype-raw, которая числилась в проекте, но ни разу не была подключена к рендерингу markdown, и файл стилей на 125 строк, дублирующий основной и никем не импортируемый. Пятый — две функции с абсолютно одинаковым телом в конфиге сайта (одна просто вызывала другую) и компонент-обёртку для смены темы, который не был подключён ни в одном layout проекта. Я даже не помнил, что этот файл существует.
Каждый раунд заканчивался одинаково: правка, полная сборка проекта, зелёный результат, коммит. Ни одного отката за все пять итераций.
Итог по цифрам
Пять коммитов, каждый проверен полной сборкой перед публикацией:
- -43 зависимости удалены из проекта — 26 неиспользуемых UI-компонентов, 15 остального конструктора, плюс
rehype-rawи обёртка темы - -2985 строк в репозитории целиком, -839 если убрать служебный lock-файл и считать только собственный код
- 7 мёртвых файлов постов и 5 неиспользуемых картинок-заглушек удалены
- 1 настоящий баг — два security/SEO-валидатора, которые год как молча пропускали треть контента блога
Самое ценное здесь — не строки, а именно баг с валидатором. Он не сломал ничего видимого, не уронил сборку, не вызвал ни одной жалобы. Он просто тихо перестал делать свою работу — и продолжал бы молчать, если бы я не решил доверить проверку не своей памяти, а инструменту, которому специально поручено сомневаться в каждой строчке.
Pro Tips: Как получить максимум от такого аудита
- Не верь находке на слово — воспроизведи её. Прежде чем чинить баг с валидатором, я запустил его вручную и увидел ложную зелёную галочку своими глазами. Это разница между "агент так говорит" и "я это видел".
- Гоняй сборку после каждого удаления, а не после всех сразу. Откатить один неудачный коммит в разы проще, чем распутывать пачку из сорока изменений.
- Повторяй аудит несколько раз подряд. Первый проход почти всегда ловит самое очевидное — зависимости. Настоящие находки прячутся глубже и всплывают только после того, как убрано верхнее наслоение мусора.
Common Pitfalls: Где легко ошибиться
- Удалить зависимость, не проверив, не является ли она чужим скрытым требованием. Библиотека для подсветки кода в постах выглядела неиспользуемой по прямому импорту, но была обязательным условием для другого пакета, который реально рендерит код в статьях. Удали её не глядя — сломал бы каждую статью в блоге.
- Принять зелёную галочку валидатора за факт, а не за гипотезу. "Все посты прошли проверку" может означать "все файлы, которые я умею находить, прошли", а вовсе не "все посты проверены". Это ровно та ловушка, в которую я сам попадал год.