$ sudo teach IT

Весь курс мы работали с Claude Code как с интерактивным собеседником: запустили сессию, поговорили, вышли. Но у многих задач нет собеседника. Нужно проверить сотню файлов на одно и то же. Нужно, чтобы в CI на каждый pull request запускалась проверка. Нужно вставить Claude в середину шелл-конвейера между git diff и файлом отчёта.

Для этого есть headless-режим: флаг -p превращает Claude Code в обычную утилиту командной строки. Она читает stdin, пишет в stdout, возвращает код завершения. Всё, что вы умеете делать с утилитами в шелле, применимо и к ней — пайпы, циклы, редиректы, подстановка в скрипты.

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

Вы умеете настраивать разрешения, писать скиллы и подключать MCP — всё это работает и в headless-режиме, что порождает отдельный вопрос: а нужно ли оно в CI. Разберём флаг -p, пайпы и лимит stdin, флаги разрешений --allowedTools и --permission-mode, продолжение диалогов через --continue и --resume, а также режим --bare для воспроизводимых запусков.

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

  • Как запустить Claude Code без интерактивной сессии флагом p
  • Как передавать данные через пайп и какой у stdin лимит
  • Как заранее разрешить инструменты, чтобы запуск не ждал подтверждения
  • Зачем нужен режим -bare и чем он опасен без него в CI
  • Как продолжать разговор между отдельными запусками

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

  • Headless-режим — неинтерактивный запуск Claude Code флагом -p (или --print): задача передаётся аргументом, ответ печатается в stdout.
  • -allowedTools — флаг, перечисляющий инструменты, которые выполняются без запроса разрешения.
  • -permission-mode — флаг, задающий режим разрешений для всей сессии.
  • -bare — режим, отключающий автоподхват хуков, скиллов, подагентов, плагинов, MCP-серверов и CLAUDE.md.
  • -append-system-prompt — флаг, добавляющий текст к системному промпту, не заменяя его.

Первый запуск без интерфейса

Самая простая форма — вопрос аргументом:

claude -p "Что делает модуль аутентификации в этом проекте?"

Claude выполнит задачу и напечатает ответ в stdout — без интерфейса и ожидания ввода.

Важное для скриптов: Claude Code возвращает код 0 при успехе и ненулевой при неудаче, так что ветвиться по статусу можно как обычно. Неверный флаг даёт ошибку в stderr до начала работы, а сбой внутри запуска печатается как результат в stdout.

Скиллы и пользовательские команды здесь работают: включите /skill-name в текст промпта, и Claude Code развернёт его перед запуском. Встроенные команды интерфейса, вроде /login, недоступны.

Пайпы: Claude как звено конвейера

Headless-режим читает stdin. Классический пример — объяснить ошибку сборки:

cat build-error.txt | claude -p 'кратко объясни корневую причину этой ошибки сборки' > output.txt

Или встроить Claude в npm-скрипт как линтер опечаток:

{
  "scripts": {
    "lint:claude": "git diff main | claude -p \"ты линтер опечаток. для каждой опечатки в этом диффе выведи filename:line на одной строке и описание на следующей. больше ничего не выводи.\""
  }
}

Скрытая польза: раз дифф приходит через пайп, Claude не нужны права на выполнение Bash. Меньше прав — меньше рисков.

Ограничение: stdin через пайп ограничен 10 МБ. При превышении Claude Code завершается с понятной ошибкой и ненулевым кодом. Если данных больше, запишите их в файл и укажите путь к файлу в промпте.

Разрешения: чтобы запуск не завис

Главная ловушка headless-режима: запуск, который ждёт ответа на запрос разрешения, а ответить некому.

Для -p встроенный стартовый режим разрешений — Manual, на любом плане. Значит, нужно явно указать, что разрешено. Первый способ — перечислить инструменты:

claude -p "Запусти тесты и почини падения" \
  --allowedTools "Bash,Read,Edit"

Можно и точнее, синтаксисом правил разрешений:

claude -p "Посмотри staged-изменения и сделай коммит" \
  --allowedTools "Bash(git diff *)" "Bash(git log *)" "Bash(git status *)" "Bash(git commit *)"

Обратите внимание на пробел перед звёздочкой: Bash(git diff *) разрешает команды, начинающиеся с git diff. Без пробела Bash(git diff*) совпал бы ещё и с git diff-index.

Второй способ — задать режим разрешений для всей сессии:

# Классификатор проверяет действия вместо вас
claude -p "Обнови пины зависимостей и прогони тесты" --permission-mode auto

# Правки без запроса
claude -p "Примени фиксы линтера" --permission-mode acceptEdits

# Заперто: разрешено только то, что явно в правилах
claude -p "Проверь конфигурацию" --permission-mode dontAsk

Режим dontAsk уместен в CI: Claude Code запретит всё, чего нет в правилах permissions.allow или в наборе read-only команд.

Режим --bare: одинаковый результат на любой машине

Без специальных флагов claude -p загружает ровно тот же контекст, что и интерактивная сессия: хуки, скиллы, подагентов, плагины, MCP-серверы, авто-память, CLAUDE.md. В CI это проблема сразу с двух сторон.

Со стороны воспроизводимости: хук в ~/.claude коллеги или MCP-сервер из .mcp.json проекта изменят поведение запуска, и результат на разных машинах будет разным.

Со стороны безопасности — серьёзнее. Без --bare запуск с -p выполняет хуки из .claude/settings.json проекта и подключает серверы из его .mcp.json даже в папке, которой вы никогда не доверяли. Диалога доверия в режиме -p нет, и отдельного подтверждения по каждому серверу тоже.

Решение — флаг --bare:

claude --bare -p "Кратко перескажи README.md" --allowedTools "Read"

Он отключает автоподхват хуков, скиллов, пользовательских команд, подагентов, плагинов, MCP-серверов, авто-памяти и CLAUDE.md. Заодно ускоряет старт. В bare-режиме у Claude остаются Bash, чтение и правка файлов.

Одна деталь, которая ломает первый запуск: bare-режим не использует вход по подписке. Для Anthropic API нужно задать ANTHROPIC_API_KEY в окружении.

Если что-то из контекста всё же нужно, это передаётся явно:

Что загрузить Чем
Дополнения к системному промпту --append-system-prompt, --append-system-prompt-file
Настройки --settings <файл-или-json>
MCP-серверы --mcp-config <файл-или-json>
Плагин --plugin-dir <путь>, --plugin-url <url>

Документация отмечает, что --bare — рекомендуемый режим для скриптов и что в будущем он станет режимом по умолчанию для -p.

Продолжение разговора между запусками

Каждый вызов claude -p — отдельная сессия, но их можно связать. Флаг --continue продолжает самый недавний разговор:

claude -p "Проверь кодовую базу на проблемы с производительностью"
claude -p "Теперь сосредоточься на запросах к базе" --continue
claude -p "Собери сводку по всем найденным проблемам" --continue

Если параллельных разговоров несколько, надёжнее сохранить идентификатор сессии и возвращаться к нужному по нему. Это пригодится в следующем уроке, где мы разберём JSON-вывод.

Полезно и --append-system-prompt — он добавляет инструкции, не заменяя поведение Claude Code по умолчанию:

gh pr diff "$1" | claude -p \
  --append-system-prompt "Ты инженер по безопасности. Ищи уязвимости." \
  --output-format json

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

  • Забыть про разрешения. Для p стартовый режим — Manual на любом плане. Без -allowedTools или -permission-mode запуск упрётся в запрос, отвечать на который некому.
  • Запускать в CI без --bare. Обычный p подхватывает хуки и MCP-серверы проекта даже в недоверенной папке, без диалога доверия. Результат становится невоспроизводимым, а запуск — небезопасным.
  • Задать --bare и не задать ANTHROPIC_API_KEY. В bare-режиме Claude Code не читает OAuth-креденшелы и связку ключей, поэтому вход по подписке не работает.
  • Пропихивать в пайп больше 10 МБ. Лимит stdin — 10 МБ, при превышении запуск падает с ошибкой. Большие данные пишите в файл и указывайте путь в промпте.

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

  • Работают ли мои скиллы в режиме p? — Да, включите /skill-name в текст промпта, и Claude Code развернёт его перед запуском. Встроенные команды интерфейса вроде /login недоступны.
  • Как понять в скрипте, что запуск удался? — По коду завершения: 0 при успехе, ненулевой при неудаче. Ошибки неверных флагов идут в stderr до старта, сбои внутри запуска печатаются как результат в stdout.
  • Чем -bare отличается от -safe-mode? — -bare дополнительно урезает набор инструментов до Bash, чтения и правки ради скорости. -safe-mode оставляет встроенные инструменты и разрешения работать штатно и только отключает пользовательские настройки.
  • Что будет с фоновой Bash-задачей после завершения запуска? — Её шелл завершается примерно через пять секунд после того, как Claude вернул результат и stdin закрылся.
  • Можно ли продолжить конкретный разговор, а не последний? — Да, флагом -resume с идентификатором сессии. Запускать команды можно из разных каталогов: Claude Code найдёт сессию по идентификатору.

Итоги

Отличный старт финальной главы: Claude Code перестал быть только собеседником и стал утилитой командной строки. Вы умеете запускать его флагом -p, кормить данными через пайп с учётом лимита в 10 МБ, заранее разрешать инструменты через --allowedTools и --permission-mode, изолировать запуск режимом --bare и связывать вызовы через --continue.

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

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

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