Странствующий техник
Подкинуть работёнку
Назад к статьям
DEV22 июля 2026·8 MIN

Аудит неиспользуемых зависимостей в Claude Code: плагин ponytail и -2985 строк кода

Как найти и удалить мёртвый код и неиспользуемые npm-пакеты с помощью плагина ponytail для Claude Code — в отличие от knip и архивированного depcheck, объясняет находки, а не просто их перечисляет. Команды /ponytail-audit и найденный по пути security-баг.

Нужен был способ найти неиспользуемые npm-пакеты и мёртвый код в Next.js-проекте, не перебирая вручную каждый импорт. Стандартная рекомендация в русскоязычных гайдах — depcheck, но этот пакет официально архивирован в 2025 году, а актуальная замена (knip) — статический анализатор без понимания контекста задачи: находит неиспользуемое, но не объясняет, безопасно ли удалять, и не проверяет transitive-зависимости. Решение — плагин ponytail для Claude Code с командой аудита /ponytail-audit, которая сканирует весь репозиторий через LLM-агента и выдаёт список того, что можно смело удалить, с обоснованием каждой находки. За один прогон на своём лендинге я вычистил 43 неиспользуемые зависимости, 2985 строк мёртвого кода — и заодно обнаружил security-валидатор блога, который год как ничего не проверял.

Ниже — установка ponytail за три команды, разбор всех шести команд плагина с примерами использования, и полная история аудита на реальном проекте: от первой чистки package.json до найденного бага в собственных скриптах.

Стек AI-инструментов

  • Claude Code — CLI-агент, в котором запускался весь аудит
  • ponytail — плагин-маркетплейс с командами /ponytail, /ponytail-audit, /ponytail-review, /ponytail-debt, /ponytail-gain, /ponytail-help
  • grep / pnpm — для верификации находок агента вручную, прежде чем что-то удалять

Установка ponytail за три команды

Ponytail устанавливается прямо в диалоге с Claude Code, без правки конфигов вручную:

/plugin marketplace add DietrichGebert/ponytail
/plugin install ponytail@ponytail
/reload-plugins

После перезагрузки плагинов становятся доступны все команды ниже. По умолчанию активен режим full — сбалансированная строгость между "ничего не трогать" и "удалять всё подряд".

Гайд по командам ponytail для аудита кода

/ponytail — постоянный режим для кодинга

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

/ponytail lite    — построит что просили, но подскажет более простую альтернативу
/ponytail          — режим по умолчанию, вся цепочка вопросов обязательна
/ponytail ultra    — максимальный YAGNI, оспаривает само требование в ответе

Пример разницы на задаче "добавь кэш для этих API-ответов":

  • lite: сделает кэш-класс, но добавит — "кстати, functools.lru_cache решает это в одну строку".
  • full: сразу поставит @lru_cache(maxsize=1000), ничего лишнего не напишет.
  • ultra: откажется добавлять кэш вообще, пока профайлер не покажет, что он нужен.

/ponytail-review — ревью текущего diff на переусложнение

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

L42: yagni: factory на один продукт. Инлайн.
L88: stdlib: свой email-валидатор на 30 строк. re.match с одним regex.

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

/ponytail-audit — аудит всего репозитория на мёртвый код

Ключевая команда для этой истории. В отличие от /ponytail-review, сканирует не diff, а весь проект целиком: неиспользуемые зависимости в package.json, мёртвые файлы, дублирующийся код, конфиги без единого читателя. Тоже только список, ничего не применяет автоматически.

/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

Первым делом агент открыл package.json и увидел 26 пакетов @radix-ui/react-* — accordion, dialog, dropdown-menu, tooltip, весь набор компонентов. Мой сайт — терминал в стиле Pip-Boy из Fallout, без модалок и тултипов. Явное подозрение на мёртвый вес, но проверять нужно строго: одно неверное удаление ломает сборку.

Агент прогнал 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: находим сломанный security-валидатор блога

На втором проходе я попросил ponytail не искать зависимости, а посмотреть на код репозитория целиком — восемь параллельных под-агентов, каждый со своим фокусом: корректность логики, повторное использование, эффективность, соответствие внутренним правилам проекта.

Один из них зацепился за три кастомных валидатора, встроенных в pnpm build: проверку канонических ссылок, HTML-безопасность блога, стилевую политику. Мой блог когда-то переехал с формата .html на .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
}

Три последних поста существовали только как .mdx, без .html рядом. Валидатор пытался прочитать несуществующий файл, ловил исключение — и молча пропускал пост. Я запустил проверку вручную и увидел зелёную строку "All blog posts passed security validation" — притом что треть контента вообще не сканировалась на встроенный исполняемый код и обработчики событий в разметке.

Как чинили: обе стороны разрыва, а не одну

Проверка дерева показала: семь из десяти директорий постов тащили мёртвый index.html рядом с рабочим .mdx — остаток старой миграции. Удалил все семь, переписал оба валидатора на чтение frontmatter через gray-matter:

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: дублирование, orphan-файлы, мёртвые компоненты

Каждый следующий прогон /ponytail-audit находил что-то новое — не потому что код портился на глазах, а потому что первый проход искал явно мёртвые зависимости, а не связи между файлами.

Раунд 3 вскрыл дублирование в хелпере блога: две функции параллельно читали один и тот же файл поста с разным кастингом типов. Свёл к одной внутренней функции с двумя тонкими публичными вызовами. Раунд 4 нашёл зависимость rehype-raw, ни разу не подключённую к рендерингу markdown, и orphan-файл стилей на 125 строк. Раунд 5 — две функции с идентичным телом в конфиге сайта и компонент-обёртку темы, ни разу не смонтированную ни в одном layout.

Итог по цифрам

Пять коммитов, каждый проверен полной сборкой перед публикацией:

  • -43 зависимости из package.json
  • -2985 строк в репозитории целиком, -839 без служебного lock-файла
  • 7 мёртвых файлов постов и 5 неиспользуемых картинок-заглушек
  • 1 настоящий security-баг — два валидатора, которые год как молча пропускали треть контента блога

Похожая история с ловлей неочевидного бага в проде описана в посте про плагин opencode-rate-limit — там баг был не в мёртвом коде, а в API-лимитах моделей.

Pro Tips: Prompting and Execution

  • Не верь находке агента на слово — воспроизведи её. Перед тем как чинить баг с валидатором, я запустил его вручную и увидел ложную зелёную галочку своими глазами.
  • Гоняй сборку после каждого удаления, а не после всех сразу. Откатить один неудачный коммит проще, чем распутывать пачку из сорока изменений.
  • Повторяй /ponytail-audit несколько раз подряд. Первый проход ловит явные зависимости, настоящие структурные находки всплывают только после того, как убран верхний слой мусора.

Common Pitfalls

  • Удалить зависимость, не проверив transitive peer. Библиотека для подсветки кода в постах выглядела неиспользуемой по прямому импорту, но была обязательным peer-условием для другого пакета — удали её не глядя, сломались бы все посты с кодом.
  • Принять зелёную галочку валидатора за факт, а не за гипотезу. "Все посты прошли проверку" может означать "все файлы, которые я умею находить, прошли" — а не "все посты реально проверены".
  • Полагаться только на автоматическое удаление без ручного grep. Агент даёт список кандидатов, но финальное решение и проверку компиляции стоит делать самостоятельно.
  • Ждать от статического анализатора того же, что даёт LLM-агент. Инструменты вроде knip находят неиспользуемые файлы и экспорты быстро и без токенов, но не объясняют, почему что-то мёртвое и безопасно ли его удалять — для этого решения нужен контекст, который статический анализ не видит.

Идентификация

Денис Заплахов

Исследую Пустоши рутинных задач: настраиваю AI-инструменты, создаю скиллы и тестирую нейросети на реальных кейсах.

Досье в Pip-Boy 3000
© 2026 79z.ru · Странствующий техник