$ sudo teach IT

Скилл без аргументов умеет ровно одно: делать то, что в нём написано. Скилл с аргументами становится инструментом: /fix-issue 123 и /fix-issue 456 запускают один текст, применённый к разным задачам.

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

Что уже умеем и что добавим

Вы умеете создавать скилл, задавать description, управлять вызовом и ограничивать инструменты через allowed-tools. Пока содержимое скилла было статичным. Теперь добавим в него переменную часть: пользовательский ввод и вывод shell-команд.

Что вы узнаете в рамках статьи

  • Как передать в скилл весь ввод целиком через $ARGUMENTS
  • Как работают позиционные плейсхолдеры $0, $1, $2 и почему отсчёт идёт с нуля
  • Как объявить именованные аргументы полем arguments и подсказать их через argument-hint
  • Что происходит, когда аргументов передали меньше, чем ожидает скилл
  • Как встроить в скилл вывод shell-команды через !команда`` и не сломать инвокацию

Ключевые термины

  • Плейсхолдер — метка в теле скилла ($ARGUMENTS, $1, $issue), на место которой Claude Code подставляет значение перед отправкой содержимого модели.
  • Позиционный аргумент — аргумент, определяемый порядком при вызове, а не именем. $0 — первый, $1 — второй.
  • Именованный аргумент — позиционный аргумент, которому поле arguments дало имя; обращение к нему идёт как $имя.
  • Динамический контекст — вывод shell-команды, выполненной до отправки содержимого модели и вставленной в текст вместо плейсхолдера.
  • Служебная подстановка — переменная Claude Code вида ${CLAUDE_SKILL_DIR}, разворачиваемая в путь или значение сессии.

Весь ввод целиком и позиционные плейсхолдеры

Самый простой способ — $ARGUMENTS. Он разворачивается в полную строку аргументов ровно так, как вы её ввели.

---
description: Fix a GitHub issue by number
argument-hint: [issue-number]
---

Find and fix issue $ARGUMENTS. Follow the coding standards of the repository.

Вызов /fix-issue 123 даст модели текст Find and fix issue 123. — остался только результат подстановки.

Когда аргументов несколько, нужны позиционные плейсхолдеры: $ARGUMENTS[N] или короткая форма $N. Здесь главная ловушка урока: отсчёт идёт с нуля. $0 — первый аргумент, $1 — второй, $2 — третий. Пример из документации:

---
name: migrate-component
description: Migrate a component from one language to another
---

Migrate the $0 component from $1 to $2.
Preserve all existing behavior and tests.

Вызов /migrate-component SearchBar JavaScript TypeScript превращается в Migrate the SearchBar component from JavaScript to TypeScript. Привычка из shell, где $1 — первый параметр, работает против вас: перепутав нумерацию, вы получите корректный, но бессмысленный промпт.

Разбор аргументов — shell-подобный, с учётом кавычек. Вызов /my-skill "hello world" second даст $0 = hello world и $1 = second. Без кавычек hello и world стали бы двумя аргументами.

Именованные аргументы и подсказка в автодополнении

Читать $0 и $2 в длинном скилле неудобно. Поле arguments позволяет дать позициям имена — строкой через пробел или YAML-списком:

---
description: Open a PR for an issue
arguments: [issue, branch]
argument-hint: [issue-number] [branch-name]
---

Теперь в теле пишется $issue и $branch. Важно, что именно изменилось: сопоставление всё равно позиционное. С arguments: [issue, branch] плейсхолдер $issue получит первый аргумент, $branch — второй, просто потому что так они объявлены. Это не «ключ=значение»: вызвать /open-pr branch=main issue=42 нельзя. Имена — это читаемость, а не свобода порядка.

Поле argument-hint на подстановку не влияет: это подсказка в автодополнении при наборе /. Формат — текст в скобках: [issue-number] или [filename] [format].

Когда аргументов не хватает

Здесь два разных поведения, и различие важное.

Индексный плейсхолдер без соответствующего аргумента остаётся в тексте как есть. Если скилл содержит $2, а вы передали один аргумент, модель увидит буквальные символы $2.

Именованный плейсхолдер без аргумента разворачивается в пустую строку. С arguments: [issue, branch] и вызовом /open-pr 42 строка Base branch: $branch придёт модели как Base branch:.

Ещё подстраховка: если аргументы переданы, но ни один плейсхолдер их не получил, Claude Code дописывает в конец содержимого строку ARGUMENTS: <ваш ввод>. Ввод не теряется.

Два мелких правила. Литеральный доллар перед цифрой или словом ARGUMENTS экранируется слэшем: чтобы в тексте осталось $1.00, пишите \$1.00. И наоборот: значение аргумента, содержащее текст вроде $1, вставится как литерал и повторно разворачиваться не будет.

Служебные подстановки и запуск скриптов

Кроме аргументов доступны переменные самого Claude Code: ${CLAUDE_SESSION_ID} — ID сессии, ${CLAUDE_EFFORT} — уровень усилий, ${CLAUDE_PROJECT_DIR} — корень проекта, ${CLAUDE_SKILL_DIR} — директория, где лежит SKILL.md этого скилла.

Последняя решает конкретную проблему. Скиллу часто нужен вспомогательный скрипт из своей папки, но рабочая директория сессии может быть любой, и относительный путь сломается. Ключевой момент: ${CLAUDE_SKILL_DIR} подставляется и в теле скилла, и в Bash-правилах allowed-tools. Предодобрение совпадает с реальной командой, и скрипт запускается без запроса разрешения:

---
name: render-chart
description: Render a chart from a CSV file
allowed-tools: Bash(${CLAUDE_SKILL_DIR}/scripts/render.sh *)
---

Run `${CLAUDE_SKILL_DIR}/scripts/render.sh <csv-file>` to render the chart.

Динамический контекст: команда вместо плейсхолдера

Синтаксис !команда`` выполняет команду до того, как содержимое скилла отправится модели, и вставляет её вывод на место плейсхолдера. Claude получает готовые данные, а не задание сходить за ними.

---
description: Summarizes uncommitted changes and flags anything risky.
---

## Current changes

!`git diff HEAD`

## Instructions

Summarize the changes above in two or three bullet points, then list any risks.

Инлайн-форма распознаётся, только если ! стоит в начале строки или сразу после пробела. Написав KEY=!`date`, вы получите литеральный текст — команда не выполнится. Для нескольких команд есть многострочный блок, открытый ```!:

## Environment

```!
node --version
git status --short
```

Подстановка выполняется один раз по исходному файлу; вывод вставляется как обычный текст и повторно на плейсхолдеры не сканируется. Рабочая директория — текущая директория сессии, stderr сливается со stdout, таймаут — 2 минуты.

Самое важное — ошибки. Неуспешная команда прерывает всю инвокацию скилла, а не только свой плейсхолдер: Claude вообще не увидит содержимое, вы получите Shell command failed for pattern "...". Поэтому команду, которая штатно возвращает ненулевой код, дописывают через || true. Исключение — поисковые команды: код 1 у grep и подобных считается нормой, коды 2 и выше — ошибкой.

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

---
description: Review changes on a branch
arguments: [branch]
argument-hint: [branch-name]
---

## Diff against $branch

!`git diff main...HEAD --stat`

Review the changes above in the context of branch $branch.

Напоследок мелочь: скиллы можно «стакать» в начале одного сообщения. Ввод /write-tests /fix-issue 123 загрузит оба скилла и передаст 123 каждому.

Частые ошибки

  • Считать, что $1 — первый аргумент. Отсчёт идёт с нуля: первый — $0. Сбоя не будет, промпт просто получит не те значения.
  • Ожидать синтаксис «ключ=значение». Поле arguments даёт позициям имена, но аргументы всё равно читаются по порядку. Вызвать /skill issue=42 нельзя.
  • Ставить ! не в начале строки. KEY=!cmd`` останется буквальным текстом: перед ! стоит не пробел.
  • Не страховать команду от ненулевого кода. Одна упавшая команда обрывает всю инвокацию. Там, где ненулевой код нормален, добавляйте || true.

Наиболее частые вопросы

  • Что вернёт $3, если я передал два аргумента? — Ничего: индексный плейсхолдер без аргумента остаётся в тексте как есть, модель увидит символы $3.
  • А именованный плейсхолдер без аргумента? — Он разворачивается в пустую строку. Это ключевое отличие от индексных.
  • Куда денется ввод, если в скилле нет плейсхолдеров? — Claude Code допишет в конец содержимого строку ARGUMENTS: <ваш ввод>.
  • Как оставить в тексте знак доллара с цифрой? — Экранировать обратным слэшем: \$1.00.
  • Зачем ${CLAUDE_SKILL_DIR}, если можно указать путь? — Он резолвится одинаково при любой рабочей директории и подставляется в том числе в allowed-tools, так что скрипт запускается без запроса разрешения.

Итоги

Вы разобрали самую синтаксически плотную часть главы. Теперь вы умеете принимать в скилл весь ввод целиком, разбирать его на позиции с отсчётом с нуля, давать позициям читаемые имена через arguments, подсказывать формат вызова через argument-hint и предсказывать поведение при нехватке аргументов. Вы также знаете, как втянуть в скилл живые данные командой !...``.

Стоит потратить время на практику: здесь ошибки самые незаметные, потому что скилл не падает, а просто работает не так, как вы задумали. Пройдите тесты и соберите пару скиллов с аргументами на своём репозитории. На следующем уроке мы посмотрим на поле context: fork — способ запустить скилл в изолированном контексте, чтобы объёмная задача не засоряла основной разговор.

На следующий урок