Настройка строки состояния
Настройте пользовательскую строку состояния для мониторинга использования контекстного окна, затрат и статуса git в Claude Code
Строка состояния — это настраиваемая панель в нижней части Claude Code, которая запускает любой скрипт оболочки, который вы настроите. Она получает данные сеанса в формате JSON через stdin и отображает всё, что выводит ваш скрипт, предоставляя вам постоянный, видимый с первого взгляда обзор использования контекста, затрат, статуса git или чего-либо ещё, что вы хотите отслеживать.
Строки состояния полезны, когда вы:
- Хотите отслеживать использование контекстного окна во время работы
- Нужно отслеживать затраты сеанса
- Работаете в нескольких сеансах и нужно их различать
- Хотите, чтобы ветка git и статус всегда были видны
Строка состояния отображается в собственной строке над встроенными значками нижнего колонтитула и не заменяет их. С настроенной пользовательской строкой состояния Claude Code перестаёт показывать большинство подсказок клавиатуры нижнего колонтитула, включая esc для прерывания, ? для сочетаний клавиш и подсказку удерживайте пробел для голоса голосовой диктовки. Чтобы добавить интерактивные значки ссылок в нижний колонтитул, когда в беседе появляется ID, без написания скрипта, вместо этого настройте footerLinksRegexes.
Вот пример многострочной строки состояния, которая отображает информацию git в первой строке и цветовую полосу контекста во второй.
На этой странице описано настройка базовой строки состояния, объясняется как данные передаются из Claude Code в ваш скрипт, перечисляются все поля, которые вы можете отображать, и предоставляются готовые примеры для распространённых паттернов, таких как статус git, отслеживание затрат и полосы прогресса.
Настройка строки состояния
Используйте команду /statusline для автоматического создания скрипта Claude Code, или вручную создайте скрипт и добавьте его в ваши настройки.
Использование команды /statusline
Команда /statusline принимает инструкции на естественном языке, описывающие то, что вы хотите отображать. Claude Code генерирует файл скрипта в ~/.claude/ и автоматически обновляет ваши настройки:
/statusline show model name and context percentage with a progress bar
Одобрите запросы на редактирование файла, если Claude Code попросит разрешение во время настройки.
Ручная настройка строки состояния
Добавьте поле statusLine в ваши пользовательские настройки (~/.claude/settings.json, где ~ — это ваш домашний каталог) или настройки проекта. Установите type на "command" и укажите command на путь скрипта или встроенную команду оболочки. Для полного пошагового руководства по созданию скрипта см. Построение строки состояния пошагово.
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh",
"padding": 2
}
}
Поле command запускается в оболочке, поэтому вы также можете использовать встроенные команды вместо файла скрипта. Этот пример использует jq для разбора входных данных JSON и отображения имени модели и процента контекста:
{
"statusLine": {
"type": "command",
"command": "jq -r '\"[\\(.model.display_name)] \\(.context_window.used_percentage // 0)% context\"'"
}
}
Необязательное поле padding добавляет дополнительное горизонтальное расстояние (в символах) к содержимому строки состояния. По умолчанию 0. Это заполнение добавляется к встроенному расстоянию интерфейса, поэтому оно управляет относительным отступом, а не абсолютным расстоянием от края терминала.
Необязательное поле refreshInterval повторно запускает вашу команду каждые N секунд в дополнение к обновлениям, управляемым событиями. Минимум — 1. Установите это, когда ваша строка состояния отображает данные на основе времени, такие как часы, или когда фоновые подагенты изменяют состояние git, пока основной сеанс неактивен. Оставьте это не установленным, чтобы запускать только при событиях.
Необязательное поле hideVimModeIndicator подавляет встроенный текст -- INSERT -- под приглашением. Установите это на true, когда ваш скрипт отображает vim.mode самостоятельно, чтобы режим не отображался дважды.
Отключение строки состояния
Запустите /statusline и попросите её удалить или очистить вашу строку состояния (например, /statusline delete, /statusline clear, /statusline remove it). Вы также можете вручную удалить поле statusLine из вашего settings.json.
Построение строки состояния пошагово
Это пошаговое руководство показывает, что /statusline настраивает для вас путём ручного создания строки состояния, которая отображает текущую модель, рабочий каталог и процент использования контекстного окна.
Запуск /statusline с описанием того, что вы хотите, настраивает всё это автоматически.
Эти примеры используют скрипты Bash, которые работают на macOS и Linux. На Windows см. Конфигурация Windows для примеров PowerShell и Git Bash.
Создайте скрипт, который читает JSON и выводит результат
Claude Code отправляет данные JSON в ваш скрипт через stdin. Этот скрипт использует jq, парсер JSON командной строки, который вам может потребоваться установить, для извлечения имени модели, каталога и процента контекста, а затем выводит отформатированную строку.
Сохраните это в ~/.claude/statusline.sh (где ~ — это ваш домашний каталог, например /Users/username на macOS или /home/username на Linux):
#!/bin/bash
# Read JSON data that Claude Code sends to stdin
input=$(cat)
# Extract fields using jq
MODEL=$(echo "$input" | jq -r '.model.display_name')
DIR=$(echo "$input" | jq -r '.workspace.current_dir')
# The "// 0" provides a fallback if the field is null
PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
# Output the status line - ${DIR##*/} extracts just the folder name
echo "[$MODEL] 📁 ${DIR##*/} | ${PCT}% context"
Сделайте его исполняемым
Отметьте скрипт как исполняемый, чтобы ваша оболочка могла его запустить:
chmod +x ~/.claude/statusline.sh
Добавьте в настройки
Скажите Claude Code запустить ваш скрипт как строку состояния. Добавьте эту конфигурацию в ~/.claude/settings.json, которая устанавливает type на "command" (означает «запустить эту команду оболочки») и указывает command на ваш скрипт:
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh"
}
}
Ваша строка состояния появляется в нижней части интерфейса. Claude Code автоматически перезагружает настройки и запускает ваш скрипт сразу же после сохранения файла.
Как работают строки состояния
Claude Code запускает ваш скрипт с данными сеанса JSON на stdin и отображает всё, что скрипт выводит в stdout.
Когда это обновляется
Ваш скрипт запускается один раз при запуске сеанса, включая случаи, когда вы возобновляете его. После этого он запускается снова, когда:
- Поступает новое сообщение ассистента
- Завершается
/compact - Изменяется режим разрешений
- Переключается режим Vim
- Вы изменяете
commandв параметрахstatusLine - Истекает таймер
refreshInterval, если вы его установили - Окно ограничения скорости в данных, которые получил ваш скрипт, достигает времени
resets_at - Тёплый кэш подсказок в данных, которые получил ваш скрипт, достигает времени
expires_at
Claude Code дебаунсит обновления на 300 мс, поэтому быстрые изменения объединяются вместе и ваш скрипт запускается один раз после того, как изменения прекратятся. Изменение самой command пропускает дебаунс: Claude Code запускает новую команду сразу же. Если новое обновление срабатывает, пока ваш скрипт всё ещё работает, Claude Code отменяет выполняющийся скрипт. Если вы отредактируете свой скрипт, изменения появятся в следующий раз, когда триггер обновления его повторно запустит.
Триггеры, управляемые событиями, могут затихнуть, когда основной сеанс неактивен, например, пока координатор ждёт фоновых подагентов. Чтобы сохранить текущими сегменты на основе времени или из внешних источников во время периодов неактивности, установите refreshInterval для также повторного запуска команды на фиксированный таймер.
Что может выводить ваш скрипт
- Несколько строк: каждый оператор
echoилиprintотображается как отдельная строка. См. пример многострочности. - Цвета: используйте коды экранирования ANSI, такие как
\033[32mдля зелёного (терминал должен их поддерживать). См. пример статуса git. - Ссылки: используйте последовательности экранирования OSC 8 для создания кликабельного текста (Cmd+клик на macOS, Ctrl+клик на Windows/Linux). Требуется терминал, поддерживающий гиперссылки, такой как iTerm2, Kitty или WezTerm. См. пример кликабельных ссылок.
Размер вывода в соответствии с терминалом
Claude Code захватывает вывод вашего скрипта вместо прямого подключения к терминалу, поэтому tput cols и определение ширины на уровне языка не могут прочитать размер терминала изнутри скрипта. Вместо этого читайте переменные окружения COLUMNS и LINES. Claude Code устанавливает эти значения в соответствии с текущими размерами терминала перед запуском вашего скрипта.
Строка состояния работает локально и не потребляет токены API. Она временно скрывается во время определённых взаимодействий с пользовательским интерфейсом, включая предложения автодополнения, меню справки и запросы разрешений.
Доступные данные
Claude Code отправляет следующие поля JSON в ваш скрипт через stdin:
| Поле | Описание |
|---|---|
model.id, model.display_name |
Текущий идентификатор модели и отображаемое имя |
cwd, workspace.current_dir |
Текущий рабочий каталог. Оба поля содержат одно и то же значение; workspace.current_dir предпочтительнее для согласованности с workspace.project_dir. |
workspace.project_dir |
Каталог, в котором был запущен Claude Code, который может отличаться от cwd, если рабочий каталог изменяется во время сеанса |
workspace.added_dirs |
Дополнительные каталоги, добавленные через /add-dir или --add-dir. Пустой массив, если ничего не было добавлено |
workspace.git_worktree |
Имя Git worktree, когда текущий каталог находится внутри связанного worktree, созданного с помощью git worktree add. Отсутствует в основном рабочем дереве. Заполняется для любого git worktree, в отличие от worktree.*, который присутствует только во время сеанса в worktree сеансе |
workspace.repo.host, workspace.repo.owner, workspace.repo.name |
Идентификация репозитория, проанализированная из удалённого хранилища origin, например "github.com", "anthropics", "claude-code". Отсутствует вне git репозитория или когда удалённое хранилище origin не настроено. Для проекта gitlab.com, вложенного в подгруппы, owner — это полный путь пространства имён с косыми чертами, например "group/subgroup". До версии 2.1.260 workspace.repo отсутствовал для этих проектов |
cost.total_cost_usd |
Предполагаемая стоимость сеанса в USD, вычисляется на клиенте по цене списка, если не действует таблица modelPricing. Может отличаться от вашего фактического счёта. Сбрасывается на $0 при запуске нового сеанса с помощью /clear. До версии 2.1.211 общая стоимость переносилась после /clear |
cost.total_duration_ms |
Общее реальное время, в течение которого сеанс работал, в миллисекундах. Накапливается при возобновлении и не включает время, когда сеанс не работает |
cost.total_api_duration_ms |
Общее время, потраченное на ожидание ответов API в миллисекундах |
cost.total_lines_added, cost.total_lines_removed |
Строки кода, которые были изменены |
context_window.total_input_tokens, context_window.total_output_tokens |
Количество токенов, находящихся в контекстном окне, из последнего ответа API. Входные данные включают чтение и запись кэша |
context_window.context_window_size |
Максимальный размер контекстного окна в токенах. По умолчанию 200000 или 1000000 для моделей с расширенным контекстом. |
context_window.used_percentage |
Предварительно рассчитанный процент использованного контекстного окна |
context_window.remaining_percentage |
Предварительно рассчитанный процент оставшегося контекстного окна |
context_window.current_usage |
Подсчёты токенов из последнего вызова API, описанные в полях контекстного окна |
exceeds_200k_tokens |
Превышает ли общее количество токенов (входные, кэшированные и выходные токены в сумме) из последнего ответа API 200k. Это фиксированный порог независимо от фактического размера контекстного окна. |
fast_mode |
Включен ли быстрый режим для сеанса |
effort.level |
Текущий уровень рассуждений (low, medium, high, xhigh или max). Отражает текущее значение сеанса, включая изменения /effort во время сеанса. Ultracode не является отдельным уровнем и сообщается как xhigh. Отсутствует, когда текущая модель не поддерживает параметр effort |
thinking.enabled |
Включено ли расширенное мышление для сеанса |
rate_limits.five_hour.used_percentage, rate_limits.seven_day.used_percentage |
Процент лимита скорости за 5 часов или 7 дней, потреблённый от 0 до 100 |
rate_limits.five_hour.resets_at, rate_limits.seven_day.resets_at |
Секунды эпохи Unix, когда окно лимита скорости за 5 часов или 7 дней сбрасывается |
rate_limits.spend_limit.used_percentage, rate_limits.spend_limit.resets_at |
За шлюзом Claude apps процент использованного лимита расходов, который применяется к вам, и секунды эпохи Unix, когда его период сбрасывается. Процент варьируется от 0 до 100 или выше 100, когда вы превышаете лимит. Требуется Claude Code v2.1.251 или позже |
prompt_cache |
Статистика prompt cache сеанса для основного разговора: коэффициент попаданий, промахи и является ли кэш тёплым. Смотрите поля prompt cache для каждого поля. Отсутствует до первого ответа API основного разговора. Требуется Claude Code v2.1.251 или позже |
session_id |
Уникальный идентификатор сеанса |
session_name |
Имя сеанса. Использует пользовательское имя, установленное с флагом --name или /rename, когда оно существует, в противном случае — автоматически сгенерированный заголовок сеанса. Имя отображения по умолчанию, например my-app-3f, не заполняет это поле. Отсутствует, когда сеанс не имеет ни пользовательского имени, ни автоматически сгенерированного заголовка |
prompt_id |
UUID, идентифицирующий пользовательский запрос, который в настоящее время обрабатывается. Совпадает с атрибутом prompt.id на событиях OpenTelemetry. Отсутствует до первого ввода пользователя. Требуется Claude Code v2.1.196 или позже |
transcript_path |
Путь к файлу стенограммы разговора |
version |
Версия Claude Code |
output_style.name |
Имя текущего стиля вывода |
vim.mode |
Текущий режим vim (NORMAL, INSERT, VISUAL или VISUAL LINE), когда режим vim включен |
agent.name |
Имя агента при запуске с флагом --agent или настроенными параметрами агента |
pr.number, pr.url |
Открытый pull request для текущей ветки. Отражает значок PR в нижней строке состояния. В репозитории с удалённым хранилищем GitLab Claude Code заполняет эти поля из открытого merge request ветки, поэтому pr.number — это номер merge request. Данные merge request требуют Claude Code v2.1.234 или позже. Отсутствует, когда не в git репозитории, до тех пор пока не будет найден pull request или merge request, или после того как он будет объединён или закрыт |
pr.review_state |
Статус проверки открытого PR: approved, pending, changes_requested или draft. Может быть независимо отсутствующим даже когда pr присутствует |
pr.kind |
mr, когда pr описывает GitLab merge request. Отсутствует для GitHub pull requests, поэтому скрипты, написанные до этого поля, продолжают работать. Для merge request Claude Code устанавливает review_state на approved, когда GitLab сообщает, что он готов к объединению, pending для любого другого открытого состояния и draft для черновика. Требуется Claude Code v2.1.234 или позже |
worktree.name |
Имя активного worktree. Присутствует только во время сеанса в worktree сеансе |
worktree.path |
Абсолютный путь к каталогу worktree |
worktree.branch |
Имя ветки Git для worktree (например, "worktree-my-feature"). Отсутствует для worktrees на основе hooks |
worktree.original_cwd |
Каталог, в котором находился Claude перед входом в worktree |
worktree.original_branch |
Ветка Git, проверенная перед входом в worktree. Отсутствует для worktrees на основе hooks |
Полная схема JSON
Ваша команда строки состояния получает эту структуру JSON через stdin:
{
"cwd": "/current/working/directory",
"session_id": "abc123...",
"session_name": "my-session",
"prompt_id": "550e8400-e29b-41d4-a716-446655440000",
"transcript_path": "/path/to/transcript.jsonl",
"model": {
"id": "claude-opus-5-5",
"display_name": "Opus"
},
"workspace": {
"current_dir": "/current/working/directory",
"project_dir": "/original/project/directory",
"added_dirs": [],
"git_worktree": "feature-xyz",
"repo": {
"host": "github.com",
"owner": "anthropics",
"name": "claude-code"
}
},
"version": "2.1.90",
"output_style": {
"name": "default"
},
"cost": {
"total_cost_usd": 0.01234,
"total_duration_ms": 45000,
"total_api_duration_ms": 2300,
"total_lines_added": 156,
"total_lines_removed": 23
},
"context_window": {
"total_input_tokens": 15500,
"total_output_tokens": 1200,
"context_window_size": 200000,
"used_percentage": 8,
"remaining_percentage": 92,
"current_usage": {
"input_tokens": 8500,
"output_tokens": 1200,
"cache_creation_input_tokens": 5000,
"cache_read_input_tokens": 2000
}
},
"exceeds_200k_tokens": false,
"prompt_cache": {
"warm": true,
"caching_observed": true,
"ttl": "1h",
"expires_at": 1738429200,
"requests": 14,
"misses": 2,
"expected_rebuilds": 1,
"hit_ratio": 0.91,
"cache_write_tokens": 352000,
"miss_recache_tokens": 310200,
"last_miss_at": 1738425230,
"last_miss_cause": {
"causes": ["tools_changed"],
"tools_added": 2,
"tools_removed": 0
},
"miss_causes": {
"tools_changed": 2
},
"recache_tokens_if_cold": 45000
},
"fast_mode": false,
"effort": {
"level": "high"
},
"thinking": {
"enabled": true
},
"rate_limits": {
"five_hour": {
"used_percentage": 23.5,
"resets_at": 1738425600
},
"seven_day": {
"used_percentage": 41.2,
"resets_at": 1738857600
},
"spend_limit": {
"used_percentage": 62.8,
"resets_at": 1740787200
}
},
"vim": {
"mode": "NORMAL"
},
"agent": {
"name": "security-reviewer"
},
"pr": {
"number": 1234,
"url": "https://github.com/anthropics/claude-code/pull/1234",
"review_state": "pending"
},
"worktree": {
"name": "my-feature",
"path": "/path/to/.claude/worktrees/my-feature",
"branch": "worktree-my-feature",
"original_cwd": "/path/to/project",
"original_branch": "main"
}
}
Поля, которые могут отсутствовать (не присутствуют в JSON):
session_name: появляется, когда пользовательское имя было установлено с--nameили/rename, или после того как существует автоматически сгенерированный заголовок сеанса. Имя отображения по умолчанию, напримерmy-app-3f, не заполняет это полеprompt_id: появляется только после первого ввода пользователяworkspace.git_worktree: появляется только когда текущий каталог находится внутри связанного git worktreeworkspace.repo: появляется только внутри git репозитория с настроенным удалённым хранилищемorigineffort: появляется только когда текущая модель поддерживает параметр reasoning effortvim: появляется только когда режим vim включенagent: появляется только при запуске с флагом--agentили настроенными параметрами агентаpr: появляется только пока открытый PR или GitLab merge request найден для текущей ветки, и удаляется после того как он будет объединён или закрыт.pr.review_stateиpr.kindмогут быть независимо отсутствующимиworktree: появляется только во время сеанса в worktree сеансе. Когда присутствует,branchиoriginal_branchтакже могут отсутствовать для worktrees на основе hooksrate_limits: появляется только для подписчиков Claude.ai Pro и Max, или за шлюзом Claude apps, который устанавливает лимит расходов для вас, и только после первого ответа API в сеансе. Каждое окно (five_hour,seven_day,spend_limit) может быть независимо отсутствующим, и Claude Code удаляет окно после того как его времяresets_atпройдёт. Используйтеjq -r '.rate_limits.five_hour.used_percentage // empty'для корректной обработки отсутствия.prompt_cache: появляется после первого ответа API основного разговора. Смотрите поля prompt cache
Поля, которые могут быть null:
context_window.current_usage:nullперед первым вызовом API в сеансе и снова после/compactдо следующего вызова API, который его переполнитcontext_window.used_percentage,context_window.remaining_percentage: могут бытьnullв начале сеанса
Обрабатывайте отсутствующие поля с условным доступом и нулевые значения с резервными значениями по умолчанию в ваших скриптах.
Поля контекстного окна
Объект context_window описывает живое контекстное окно из последнего ответа API.
- Совокупные итоги (
total_input_tokens,total_output_tokens): токены, находящиеся в контекстном окне.total_input_tokens— это суммаinput_tokens,cache_creation_input_tokensиcache_read_input_tokens;total_output_tokens— это выходные токены из последнего ответа. Оба равны0перед первым ответом API. - Использование по компонентам (
current_usage): те же подсчёты токенов, разбитые по категориям. Используйте это, когда вам нужны попадания в кэш отдельно от свежего входа.
Объект current_usage содержит:
input_tokens: входные токены в текущем контекстеoutput_tokens: выходные токены, которые были сгенерированыcache_creation_input_tokens: токены, записанные в кэшcache_read_input_tokens: токены, прочитанные из кэша
Для получения информации о том, что означают поля кэша и как они выставляются счётом, см. проверка производительности кэша.
Поле used_percentage рассчитывается только из входных токенов: input_tokens + cache_creation_input_tokens + cache_read_input_tokens. Оно не включает output_tokens.
Если вы рассчитываете процент контекста вручную из current_usage, используйте ту же формулу только для входных данных, чтобы соответствовать used_percentage.
Объект current_usage равен null перед первым вызовом API в сеансе и снова сразу после /compact до следующего вызова API, который его переполнит.
Поля prompt cache
Объект prompt_cache суммирует, как основной разговор сеанса использует prompt cache. Claude Code вычисляет его из подсчётов токенов кэша в ответах API, поэтому он работает на каждом поставщике.
Объект появляется после первого ответа API основного разговора. Claude Code не учитывает запросы подагентов в этой статистике. Требуется Claude Code v2.1.251 или позже.
Таблица перечисляет каждое поле с его значением. Временные метки — это секунды эпохи Unix, то же самое, что и rate_limits.*.resets_at. Строка состояния обычно показывает одно или два из них; warm и hit_ratio наиболее прямо суммируют состояние кэша.
| Поле | Описание |
|---|---|
warm |
Находится ли кэшированный префикс всё ещё в пределах своего TTL. false, когда последний ответ не сообщил о токенах кэша, даже если caching_observed равен true |
caching_observed |
Сообщил ли какой-либо ответ в этом сеансе о токенах кэша. false означает, что prompt caching отключен, или ваш поставщик или шлюз его не сообщает |
ttl |
Время жизни кэша текущего кэшированного префикса: "5m" или "1h" |
expires_at |
Когда кэшированный префикс выходит из своего TTL и становится холодным, в секундах эпохи. null, когда последний ответ не сообщил о токенах кэша |
requests |
Запросы API, записанные для основного разговора в этом сеансе |
misses |
Запросы, которые переобработали содержимое, которое кэш уже содержал: более 5% и по крайней мере 2000 токенов того, что запрос мог бы прочитать из кэша, без компактирования или очистки результатов инструментов, чтобы объяснить недостаток чтений из кэша |
expected_rebuilds |
Перестройки кэша, которые последовали за компактированием или очисткой старых результатов инструментов |
hit_ratio |
Токены чтения кэша как доля всех входных токенов в этом сеансе, от 0 до 1. Знаменатель учитывает чтения кэша, записи кэша и некэшированный вход. null, пока все эти подсчёты равны нулю |
cache_write_tokens |
Все токены, записанные в кэш в этом сеансе, включая начальную запись первого запроса |
miss_recache_tokens |
Токены, записанные в кэш запросами, которые учитываются как промахи |
last_miss_at |
Когда произошёл последний промах, в секундах эпохи. null, пока в сеансе нет промахов |
last_miss_cause |
Что Claude Code определил как вероятную причину последнего промаха, описанную в разделе Причина последнего промаха. Требуется Claude Code v2.1.260 или позже |
miss_causes |
Сколько диагностированных промахов этого сеанса имели каждую причину, ключ по тем же названиям причин, что и last_miss_cause. Требуется Claude Code v2.1.260 или позже |
recache_tokens_if_cold |
Токены, которые следующий запрос переполнит в кэш, если кэш к тому времени стал холодным. null сразу после компактирования или очистки старых результатов инструментов, до следующего запроса, который записывает размер переписанного разговора |
Claude Code показывает ту же статистику в терминале на строке /usage команды Prompt cache (main).
Причина последнего промаха
Объект last_miss_cause сообщает, что Claude Code определил как вероятную причину самого последнего промаха. Его массив causes содержит одно или несколько названий причин, таких как tools_changed, system_prompt_changed, ttl_expired_5m или likely_server_side. Объект равен null до первого промаха сеанса и снова всякий раз, когда Claude Code не может определить причину самого последнего промаха. Требуется Claude Code v2.1.260 или позже.
Две причины добавляют подсчёты к объекту:
tools_addedиtools_removed: сtools_changed, сколько инструментов было добавлено в запрос или удалено из негоsystem_char_delta: сsystem_prompt_changed, изменение длины системного приглашения в символах
Примеры
Эти примеры показывают распространённые паттерны строк состояния. Чтобы использовать любой пример:
- Сохраните скрипт в файл, например
~/.claude/statusline.sh(или.py/.js) - Сделайте его исполняемым:
chmod +x ~/.claude/statusline.sh - Добавьте путь в ваши настройки
Примеры Bash используют jq для разбора JSON. Python и Node.js имеют встроенный разбор JSON.
Использование контекстного окна
Отобразите текущую модель и использование контекстного окна с визуальной полосой прогресса. Каждый скрипт читает JSON из stdin, извлекает поле used_percentage и строит 10-символьную полосу, где заполненные блоки (▓) представляют использование:
#!/bin/bash
# Read all of stdin into a variable
input=$(cat)
# Extract fields with jq, "// 0" provides fallback for null
MODEL=$(echo "$input" | jq -r '.model.display_name')
PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
# Build progress bar: printf -v creates a run of spaces, then
# ${var// /▓} replaces each space with a block character
BAR_WIDTH=10
FILLED=$((PCT * BAR_WIDTH / 100))
EMPTY=$((BAR_WIDTH - FILLED))
BAR=""
[ "$FILLED" -gt 0 ] && printf -v FILL "%${FILLED}s" && BAR="${FILL// /▓}"
[ "$EMPTY" -gt 0 ] && printf -v PAD "%${EMPTY}s" && BAR="${BAR}${PAD// /░}"
echo "[$MODEL] $BAR $PCT%"
#!/usr/bin/env python3
import json, sys
# json.load reads and parses stdin in one step
data = json.load(sys.stdin)
model = data['model']['display_name']
# "or 0" handles null values
pct = int(data.get('context_window', {}).get('used_percentage', 0) or 0)
# String multiplication builds the bar
filled = pct * 10 // 100
bar = '▓' * filled + '░' * (10 - filled)
print(f"[{model}] {bar} {pct}%")
#!/usr/bin/env node
// Node.js reads stdin asynchronously with events
let input = '';
process.stdin.on('data', chunk => input += chunk);
process.stdin.on('end', () => {
const data = JSON.parse(input);
const model = data.model.display_name;
// Optional chaining (?.) safely handles null fields
const pct = Math.floor(data.context_window?.used_percentage || 0);
// String.repeat() builds the bar
const filled = Math.floor(pct * 10 / 100);
const bar = '▓'.repeat(filled) + '░'.repeat(10 - filled);
console.log(`[${model}] ${bar} ${pct}%`);
});
Статус git с цветами
Показывает ветку git с цветовыми индикаторами для подготовленных и изменённых файлов. Этот скрипт использует коды экранирования ANSI для цветов терминала: \033[32m — зелёный, \033[33m — жёлтый и \033[0m — сброс на значение по умолчанию.
Каждый скрипт проверяет, является ли текущий каталог репозиторием git, подсчитывает подготовленные и изменённые файлы и отображает цветные индикаторы:
#!/bin/bash
input=$(cat)
MODEL=$(echo "$input" | jq -r '.model.display_name')
DIR=$(echo "$input" | jq -r '.workspace.current_dir')
GREEN='\033[32m'
YELLOW='\033[33m'
RESET='\033[0m'
if git rev-parse --git-dir > /dev/null 2>&1; then
BRANCH=$(git branch --show-current 2>/dev/null)
STAGED=$(git diff --cached --numstat 2>/dev/null | wc -l | tr -d ' ')
MODIFIED=$(git diff --numstat 2>/dev/null | wc -l | tr -d ' ')
GIT_STATUS=""
[ "$STAGED" -gt 0 ] && GIT_STATUS="${GREEN}+${STAGED}${RESET}"
[ "$MODIFIED" -gt 0 ] && GIT_STATUS="${GIT_STATUS}${YELLOW}~${MODIFIED}${RESET}"
echo -e "[$MODEL] 📁 ${DIR##*/} | 🌿 $BRANCH $GIT_STATUS"
else
echo "[$MODEL] 📁 ${DIR##*/}"
fi
#!/usr/bin/env python3
import json, sys, subprocess, os
data = json.load(sys.stdin)
model = data['model']['display_name']
directory = os.path.basename(data['workspace']['current_dir'])
GREEN, YELLOW, RESET = '\033[32m', '\033[33m', '\033[0m'
try:
subprocess.check_output(['git', 'rev-parse', '--git-dir'], stderr=subprocess.DEVNULL)
branch = subprocess.check_output(['git', 'branch', '--show-current'], text=True).strip()
staged_output = subprocess.check_output(['git', 'diff', '--cached', '--numstat'], text=True).strip()
modified_output = subprocess.check_output(['git', 'diff', '--numstat'], text=True).strip()
staged = len(staged_output.split('\n')) if staged_output else 0
modified = len(modified_output.split('\n')) if modified_output else 0
git_status = f"{GREEN}+{staged}{RESET}" if staged else ""
git_status += f"{YELLOW}~{modified}{RESET}" if modified else ""
print(f"[{model}] 📁 {directory} | 🌿 {branch} {git_status}")
except:
print(f"[{model}] 📁 {directory}")
#!/usr/bin/env node
const { execSync } = require('child_process');
const path = require('path');
let input = '';
process.stdin.on('data', chunk => input += chunk);
process.stdin.on('end', () => {
const data = JSON.parse(input);
const model = data.model.display_name;
const dir = path.basename(data.workspace.current_dir);
const GREEN = '\x1b[32m', YELLOW = '\x1b[33m', RESET = '\x1b[0m';
try {
execSync('git rev-parse --git-dir', { stdio: 'ignore' });
const branch = execSync('git branch --show-current', { encoding: 'utf8' }).trim();
const staged = execSync('git diff --cached --numstat', { encoding: 'utf8' }).trim().split('\n').filter(Boolean).length;
const modified = execSync('git diff --numstat', { encoding: 'utf8' }).trim().split('\n').filter(Boolean).length;
let gitStatus = staged ? `${GREEN}+${staged}${RESET}` : '';
gitStatus += modified ? `${YELLOW}~${modified}${RESET}` : '';
console.log(`[${model}] 📁 ${dir} | 🌿 ${branch} ${gitStatus}`);
} catch {
console.log(`[${model}] 📁 ${dir}`);
}
});
Отслеживание затрат и продолжительности
Отслеживайте затраты API вашего сеанса и прошедшее время. Поле cost.total_cost_usd накапливает стоимость всех вызовов API в текущем сеансе. Поле cost.total_duration_ms измеряет общее прошедшее время с момента начала сеанса, а cost.total_api_duration_ms отслеживает только время, потраченное на ожидание ответов API.
Каждый скрипт форматирует стоимость как валюту и преобразует миллисекунды в минуты и секунды:
#!/bin/bash
input=$(cat)
MODEL=$(echo "$input" | jq -r '.model.display_name')
COST=$(echo "$input" | jq -r '.cost.total_cost_usd // 0')
DURATION_MS=$(echo "$input" | jq -r '.cost.total_duration_ms // 0')
COST_FMT=$(printf '$%.2f' "$COST")
DURATION_SEC=$((DURATION_MS / 1000))
MINS=$((DURATION_SEC / 60))
SECS=$((DURATION_SEC % 60))
echo "[$MODEL] 💰 $COST_FMT | ⏱️ ${MINS}m ${SECS}s"
#!/usr/bin/env python3
import json, sys
data = json.load(sys.stdin)
model = data['model']['display_name']
cost = data.get('cost', {}).get('total_cost_usd', 0) or 0
duration_ms = data.get('cost', {}).get('total_duration_ms', 0) or 0
duration_sec = duration_ms // 1000
mins, secs = duration_sec // 60, duration_sec % 60
print(f"[{model}] 💰 ${cost:.2f} | ⏱️ {mins}m {secs}s")
#!/usr/bin/env node
let input = '';
process.stdin.on('data', chunk => input += chunk);
process.stdin.on('end', () => {
const data = JSON.parse(input);
const model = data.model.display_name;
const cost = data.cost?.total_cost_usd || 0;
const durationMs = data.cost?.total_duration_ms || 0;
const durationSec = Math.floor(durationMs / 1000);
const mins = Math.floor(durationSec / 60);
const secs = durationSec % 60;
console.log(`[${model}] 💰 $${cost.toFixed(2)} | ⏱️ ${mins}m ${secs}s`);
});
Отображение нескольких строк
Ваш скрипт может выводить несколько строк для создания более богатого отображения.
Этот пример объединяет несколько методов: цвета на основе порогов (зелёный ниже 70%, жёлтый 70-89%, красный 90%+), полосу прогресса и информацию о ветке git. Каждый оператор print или echo создаёт отдельную строку:
#!/bin/bash
input=$(cat)
MODEL=$(echo "$input" | jq -r '.model.display_name')
DIR=$(echo "$input" | jq -r '.workspace.current_dir')
COST=$(echo "$input" | jq -r '.cost.total_cost_usd // 0')
PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
DURATION_MS=$(echo "$input" | jq -r '.cost.total_duration_ms // 0')
CYAN='\033[36m'; GREEN='\033[32m'; YELLOW='\033[33m'; RED='\033[31m'; RESET='\033[0m'
# Pick bar color based on context usage
if [ "$PCT" -ge 90 ]; then BAR_COLOR="$RED"
elif [ "$PCT" -ge 70 ]; then BAR_COLOR="$YELLOW"
else BAR_COLOR="$GREEN"; fi
FILLED=$((PCT / 10)); EMPTY=$((10 - FILLED))
printf -v FILL "%${FILLED}s"; printf -v PAD "%${EMPTY}s"
BAR="${FILL// /█}${PAD// /░}"
MINS=$((DURATION_MS / 60000)); SECS=$(((DURATION_MS % 60000) / 1000))
BRANCH=""
git rev-parse --git-dir > /dev/null 2>&1 && BRANCH=" | 🌿 $(git branch --show-current 2>/dev/null)"
echo -e "${CYAN}[$MODEL]${RESET} 📁 ${DIR##*/}$BRANCH"
COST_FMT=$(printf '$%.2f' "$COST")
echo -e "${BAR_COLOR}${BAR}${RESET} ${PCT}% | ${YELLOW}${COST_FMT}${RESET} | ⏱️ ${MINS}m ${SECS}s"
#!/usr/bin/env python3
import json, sys, subprocess, os
data = json.load(sys.stdin)
model = data['model']['display_name']
directory = os.path.basename(data['workspace']['current_dir'])
cost = data.get('cost', {}).get('total_cost_usd', 0) or 0
pct = int(data.get('context_window', {}).get('used_percentage', 0) or 0)
duration_ms = data.get('cost', {}).get('total_duration_ms', 0) or 0
CYAN, GREEN, YELLOW, RED, RESET = '\033[36m', '\033[32m', '\033[33m', '\033[31m', '\033[0m'
bar_color = RED if pct >= 90 else YELLOW if pct >= 70 else GREEN
filled = pct // 10
bar = '█' * filled + '░' * (10 - filled)
mins, secs = duration_ms // 60000, (duration_ms % 60000) // 1000
try:
branch = subprocess.check_output(['git', 'branch', '--show-current'], text=True, stderr=subprocess.DEVNULL).strip()
branch = f" | 🌿 {branch}" if branch else ""
except:
branch = ""
print(f"{CYAN}[{model}]{RESET} 📁 {directory}{branch}")
print(f"{bar_color}{bar}{RESET} {pct}% | {YELLOW}${cost:.2f}{RESET} | ⏱️ {mins}m {secs}s")
#!/usr/bin/env node
const { execSync } = require('child_process');
const path = require('path');
let input = '';
process.stdin.on('data', chunk => input += chunk);
process.stdin.on('end', () => {
const data = JSON.parse(input);
const model = data.model.display_name;
const dir = path.basename(data.workspace.current_dir);
const cost = data.cost?.total_cost_usd || 0;
const pct = Math.floor(data.context_window?.used_percentage || 0);
const durationMs = data.cost?.total_duration_ms || 0;
const CYAN = '\x1b[36m', GREEN = '\x1b[32m', YELLOW = '\x1b[33m', RED = '\x1b[31m', RESET = '\x1b[0m';
const barColor = pct >= 90 ? RED : pct >= 70 ? YELLOW : GREEN;
const filled = Math.floor(pct / 10);
const bar = '█'.repeat(filled) + '░'.repeat(10 - filled);
const mins = Math.floor(durationMs / 60000);
const secs = Math.floor((durationMs % 60000) / 1000);
let branch = '';
try {
branch = execSync('git branch --show-current', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] }).trim();
branch = branch ? ` | 🌿 ${branch}` : '';
} catch {}
console.log(`${CYAN}[${model}]${RESET} 📁 ${dir}${branch}`);
console.log(`${barColor}${bar}${RESET} ${pct}% | ${YELLOW}$${cost.toFixed(2)}${RESET} | ⏱️ ${mins}m ${secs}s`);
});
Кликабельные ссылки
Этот пример создаёт кликабельную ссылку на ваш репозиторий GitHub. Удерживайте Cmd (macOS) или Ctrl (Windows/Linux) и нажмите, чтобы открыть ссылку в браузере.
Каждый скрипт получает URL удалённого репозитория, преобразует формат SSH в HTTPS и оборачивает имя репозитория в коды экранирования OSC 8. Версия Bash использует printf '%b', которая более надёжно интерпретирует экранирующие последовательности, чем echo -e в разных оболочках:
#!/bin/bash
input=$(cat)
MODEL=$(echo "$input" | jq -r '.model.display_name')
# Convert git SSH URL to HTTPS
REMOTE=$(git remote get-url origin 2>/dev/null | sed 's/git@github.com:/https:\/\/github.com\//' | sed 's/\.git$//')
if [ -n "$REMOTE" ]; then
REPO_NAME=$(basename "$REMOTE")
# OSC 8 format: \e]8;;URL\a then TEXT then \e]8;;\a
# printf %b interprets escape sequences reliably across shells
printf '%b' "[$MODEL] 🔗 \e]8;;${REMOTE}\a${REPO_NAME}\e]8;;\a\n"
else
echo "[$MODEL]"
fi
#!/usr/bin/env python3
import json, sys, subprocess, re, os
data = json.load(sys.stdin)
model = data['model']['display_name']
# Get git remote URL
try:
remote = subprocess.check_output(
['git', 'remote', 'get-url', 'origin'],
stderr=subprocess.DEVNULL, text=True
).strip()
# Convert SSH to HTTPS format
remote = re.sub(r'^git@github\.com:', 'https://github.com/', remote)
remote = re.sub(r'\.git$', '', remote)
repo_name = os.path.basename(remote)
# OSC 8 escape sequences
link = f"\033]8;;{remote}\a{repo_name}\033]8;;\a"
print(f"[{model}] 🔗 {link}")
except:
print(f"[{model}]")
#!/usr/bin/env node
const { execSync } = require('child_process');
const path = require('path');
let input = '';
process.stdin.on('data', chunk => input += chunk);
process.stdin.on('end', () => {
const data = JSON.parse(input);
const model = data.model.display_name;
try {
let remote = execSync('git remote get-url origin', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] }).trim();
// Convert SSH to HTTPS format
remote = remote.replace(/^git@github\.com:/, 'https://github.com/').replace(/\.git$/, '');
const repoName = path.basename(remote);
// OSC 8 escape sequences
const link = `\x1b]8;;${remote}\x07${repoName}\x1b]8;;\x07`;
console.log(`[${model}] 🔗 ${link}`);
} catch {
console.log(`[${model}]`);
}
});
Использование лимита скорости
Отобразите использование лимита скорости подписки Claude.ai в строке состояния. Объект rate_limits содержит скользящее окно five_hour и еженедельное окно seven_day. Каждое окно предоставляет used_percentage (от 0 до 100) и resets_at (секунды эпохи Unix, когда окно сбрасывается).
За шлюзом приложений Claude с лимитами расходов rate_limits содержит spend_limit с теми же двумя полями для лимита расходов, который применяется к вам, за исключением того, что его used_percentage может превышать 100 после превышения лимита. Требуется Claude Code версии 2.1.251 или позже.
Объект rate_limits присутствует только для подписчиков Claude.ai Pro и Max, или за шлюзом приложений Claude с лимитами расходов, и только после первого ответа API. Каждый скрипт корректно обрабатывает отсутствующее поле:
#!/bin/bash
input=$(cat)
MODEL=$(echo "$input" | jq -r '.model.display_name')
# "// empty" produces no output when rate_limits is absent
FIVE_H=$(echo "$input" | jq -r '.rate_limits.five_hour.used_percentage // empty')
WEEK=$(echo "$input" | jq -r '.rate_limits.seven_day.used_percentage // empty')
LIMITS=""
[ -n "$FIVE_H" ] && LIMITS="5h: $(printf '%.0f' "$FIVE_H")%"
[ -n "$WEEK" ] && LIMITS="${LIMITS:+$LIMITS }7d: $(printf '%.0f' "$WEEK")%"
[ -n "$LIMITS" ] && echo "[$MODEL] | $LIMITS" || echo "[$MODEL]"
#!/usr/bin/env python3
import json, sys
data = json.load(sys.stdin)
model = data['model']['display_name']
parts = []
rate = data.get('rate_limits', {})
five_h = rate.get('five_hour', {}).get('used_percentage')
week = rate.get('seven_day', {}).get('used_percentage')
if five_h is not None:
parts.append(f"5h: {five_h:.0f}%")
if week is not None:
parts.append(f"7d: {week:.0f}%")
if parts:
print(f"[{model}] | {' '.join(parts)}")
else:
print(f"[{model}]")
#!/usr/bin/env node
let input = '';
process.stdin.on('data', chunk => input += chunk);
process.stdin.on('end', () => {
const data = JSON.parse(input);
const model = data.model.display_name;
const parts = [];
const fiveH = data.rate_limits?.five_hour?.used_percentage;
const week = data.rate_limits?.seven_day?.used_percentage;
if (fiveH != null) parts.push(`5h: ${Math.round(fiveH)}%`);
if (week != null) parts.push(`7d: ${Math.round(week)}%`);
console.log(parts.length ? `[${model}] | ${parts.join(' ')}` : `[${model}]`);
});
Кэширование дорогостоящих операций
Ваш скрипт строки состояния запускается часто во время активных сеансов. Команды, такие как git status или git diff, могут быть медленными, особенно в больших репозиториях. Этот пример кэширует информацию git во временный файл и обновляет её только каждые 5 секунд.
Имя файла кэша должно быть стабильным во время вызовов строки состояния в рамках сеанса, но уникальным для разных сеансов, чтобы одновременные сеансы в разных репозиториях не читали состояние git друг друга. Идентификаторы на основе процесса, такие как $$, os.getpid() или process.pid, изменяются при каждом вызове и нарушают кэш. Вместо этого используйте session_id из входных данных JSON: он стабилен в течение жизни сеанса и уникален для каждого сеанса.
Каждый скрипт проверяет, отсутствует ли файл кэша или он старше 5 секунд, перед запуском команд git:
#!/bin/bash
input=$(cat)
MODEL=$(echo "$input" | jq -r '.model.display_name')
DIR=$(echo "$input" | jq -r '.workspace.current_dir')
SESSION_ID=$(echo "$input" | jq -r '.session_id')
CACHE_FILE="/tmp/statusline-git-cache-$SESSION_ID"
CACHE_MAX_AGE=5 # seconds
cache_is_stale() {
[ ! -f "$CACHE_FILE" ] || \
# stat -c %Y (Linux) or stat -f %m (macOS) prints the file's last-modified
# time. The Linux form must run first: on Linux, the macOS form prints a
# filesystem report to stdout before failing, and that output would be
# captured by the command substitution and break the arithmetic.
[ $(($(date +%s) - $(stat -c %Y "$CACHE_FILE" 2>/dev/null || stat -f %m "$CACHE_FILE" 2>/dev/null || echo 0))) -gt $CACHE_MAX_AGE ]
}
if cache_is_stale; then
if git rev-parse --git-dir > /dev/null 2>&1; then
BRANCH=$(git branch --show-current 2>/dev/null)
STAGED=$(git diff --cached --numstat 2>/dev/null | wc -l | tr -d ' ')
MODIFIED=$(git diff --numstat 2>/dev/null | wc -l | tr -d ' ')
echo "$BRANCH|$STAGED|$MODIFIED" > "$CACHE_FILE"
else
echo "||" > "$CACHE_FILE"
fi
fi
IFS='|' read -r BRANCH STAGED MODIFIED < "$CACHE_FILE"
if [ -n "$BRANCH" ]; then
echo "[$MODEL] 📁 ${DIR##*/} | 🌿 $BRANCH +$STAGED ~$MODIFIED"
else
echo "[$MODEL] 📁 ${DIR##*/}"
fi
#!/usr/bin/env python3
import json, sys, subprocess, os, time
data = json.load(sys.stdin)
model = data['model']['display_name']
directory = os.path.basename(data['workspace']['current_dir'])
session_id = data['session_id']
CACHE_FILE = f"/tmp/statusline-git-cache-{session_id}"
CACHE_MAX_AGE = 5 # seconds
def cache_is_stale():
if not os.path.exists(CACHE_FILE):
return True
return time.time() - os.path.getmtime(CACHE_FILE) > CACHE_MAX_AGE
if cache_is_stale():
try:
subprocess.check_output(['git', 'rev-parse', '--git-dir'], stderr=subprocess.DEVNULL)
branch = subprocess.check_output(['git', 'branch', '--show-current'], text=True).strip()
staged = subprocess.check_output(['git', 'diff', '--cached', '--numstat'], text=True).strip()
modified = subprocess.check_output(['git', 'diff', '--numstat'], text=True).strip()
staged_count = len(staged.split('\n')) if staged else 0
modified_count = len(modified.split('\n')) if modified else 0
with open(CACHE_FILE, 'w') as f:
f.write(f"{branch}|{staged_count}|{modified_count}")
except:
with open(CACHE_FILE, 'w') as f:
f.write("||")
with open(CACHE_FILE) as f:
branch, staged, modified = f.read().strip().split('|')
if branch:
print(f"[{model}] 📁 {directory} | 🌿 {branch} +{staged} ~{modified}")
else:
print(f"[{model}] 📁 {directory}")
#!/usr/bin/env node
const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');
let input = '';
process.stdin.on('data', chunk => input += chunk);
process.stdin.on('end', () => {
const data = JSON.parse(input);
const model = data.model.display_name;
const dir = path.basename(data.workspace.current_dir);
const sessionId = data.session_id;
const CACHE_FILE = `/tmp/statusline-git-cache-${sessionId}`;
const CACHE_MAX_AGE = 5; // seconds
const cacheIsStale = () => {
if (!fs.existsSync(CACHE_FILE)) return true;
return (Date.now() / 1000) - fs.statSync(CACHE_FILE).mtimeMs / 1000 > CACHE_MAX_AGE;
};
if (cacheIsStale()) {
try {
execSync('git rev-parse --git-dir', { stdio: 'ignore' });
const branch = execSync('git branch --show-current', { encoding: 'utf8' }).trim();
const staged = execSync('git diff --cached --numstat', { encoding: 'utf8' }).trim().split('\n').filter(Boolean).length;
const modified = execSync('git diff --numstat', { encoding: 'utf8' }).trim().split('\n').filter(Boolean).length;
fs.writeFileSync(CACHE_FILE, `${branch}|${staged}|${modified}`);
} catch {
fs.writeFileSync(CACHE_FILE, '||');
}
}
const [branch, staged, modified] = fs.readFileSync(CACHE_FILE, 'utf8').trim().split('|');
if (branch) {
console.log(`[${model}] 📁 ${dir} | 🌿 ${branch} +${staged} ~${modified}`);
} else {
console.log(`[${model}] 📁 ${dir}`);
}
});
Конфигурация Windows
На Windows Claude Code запускает команды строки состояния через Git Bash, когда Git Bash установлен, или через PowerShell, когда Git Bash отсутствует.
Git Bash обрабатывает неэкранированные обратные слэши как символы экранирования, поэтому путь в стиле Windows, такой как C:\Users\username\script.mjs, достигает средства запуска скрипта с удалёнными разделителями и команда не выполняется без видимой ошибки. Записывайте пути к файлам в строке command с прямыми слэшами, как показано в примерах ниже. Сокращение ~ также работает и расширяется до вашего домашнего каталога Windows.
Чтобы запустить скрипт PowerShell как вашу строку состояния, вызовите его через powershell. Это работает независимо от того, маршрутизирует ли Claude Code команду через Git Bash или PowerShell:
{
"statusLine": {
"type": "command",
"command": "powershell -NoProfile -File C:/Users/username/.claude/statusline.ps1"
}
}
$input_json = $input | Out-String | ConvertFrom-Json
$cwd = $input_json.cwd
$model = $input_json.model.display_name
$used = $input_json.context_window.used_percentage
$dirname = Split-Path $cwd -Leaf
if ($used) {
Write-Host "$dirname [$model] ctx: $used%"
} else {
Write-Host "$dirname [$model]"
}
Или запустите скрипт Bash напрямую, когда Git Bash установлен:
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh"
}
}
#!/usr/bin/env bash
input=$(cat)
cwd=$(echo "$input" | grep -o '"cwd":"[^"]*"' | cut -d'"' -f4)
model=$(echo "$input" | grep -o '"display_name":"[^"]*"' | cut -d'"' -f4)
dirname="${cwd##*[/\\]}"
echo "$dirname [$model]"
Строки состояния подагентов
Параметр subagentStatusLine отображает пользовательское тело строки для каждого подагента, показанного на панели агентов ниже приглашения. Используйте его для замены строки по умолчанию name · description · token count на ваше собственное форматирование.
{
"subagentStatusLine": {
"type": "command",
"command": "~/.claude/subagent-statusline.sh"
}
}
Команда запускается один раз за цикл обновления со всеми видимыми строками подагентов, переданными как один объект JSON на stdin. Входные данные включают базовые поля hooks, поле columns с используемой шириной строки и массив tasks. Каждая задача имеет id, name, type, status, description, label, startTime, model, effort, contextWindowSize, tokenCount, tokenSamples и cwd.
Поле model для каждой задачи — это разрешённый ID модели, на которой выполняется задача. contextWindowSize — это контекстное окно этой модели в токенах, вычисляемое так же, как context_window.context_window_size в строке состояния основного интерфейса, поэтому вы можете отобразить процент для каждой строки из tokenCount. Оба поля требуют Claude Code v2.1.205 или более поздней версии и опускаются для задачи, модель которой ещё не разрешена.
Поле effort для каждой задачи — это уровень усилий рассуждения, установленный для этого подагента, в его frontmatter определения или при отдельном вызове. Значение — это либо одна из строк уровня усилий low, medium, high, xhigh или max, либо числовой бюджет токенов. Поле сообщает настроенное значение в том виде, в котором оно написано: если модель не поддерживает этот уровень, усилия, которые Claude Code фактически применяет, могут отличаться. Поле требует Claude Code v2.1.214 или более поздней версии и отсутствует, когда подагент наследует уровень усилий сеанса.
Напишите одну строку JSON в stdout для каждой строки, которую вы хотите переопределить, в форме {"id": "<task id>", "content": "<row body>"}. Строка content отображается как есть, включая цвета ANSI и гиперссылки OSC 8. Пропустите id задачи, чтобы сохранить отображение по умолчанию для этой строки; выведите пустую строку content, чтобы скрыть её.
Те же ворота доверия, disableAllHooks и allowManagedHooksOnly, которые применяются к statusLine, применяются здесь. Плагины могут поставлять subagentStatusLine по умолчанию в своём settings.json, но в отличие от hooks, значения плагинов не запускаются под allowManagedHooksOnly даже когда плагин принудительно включен в управляемых параметрах enabledPlugins.
Советы
- Тестирование с макетными входными данными:
echo '{"model":{"display_name":"Opus"},"workspace":{"current_dir":"/home/user/project"},"context_window":{"used_percentage":25},"session_id":"test-session-abc"}' | ./statusline.sh - Сохраняйте вывод коротким: строка состояния имеет ограниченную ширину, поэтому длинный вывод может быть обрезан или неправильно обёрнут
- Кэшируйте медленные операции: ваш скрипт запускается часто во время активных сеансов, поэтому команды, такие как
git status, могут вызвать задержку. См. пример кэширования для того, как это обработать.
Проекты сообщества, такие как ccstatusline и starship-claude, предоставляют предварительно построенные конфигурации с темами и дополнительными функциями.
Устранение неполадок
Строка состояния не отображается
- Убедитесь, что ваш скрипт исполняемый:
chmod +x ~/.claude/statusline.sh - Проверьте, что ваш скрипт выводит в stdout, а не stderr
- Запустите ваш скрипт вручную, чтобы убедиться, что он выводит результат
- В Windows с установленным Git Bash обратные слэши в пути
commandвероятно будут использованы как символы экранирования перед запуском скрипта. Используйте прямые слэши в пути. См. Конфигурация Windows. - Если
disableAllHooksустановлен наtrueвне управляемых настроек после применения приоритета настроек, Claude Code запускает толькоstatusLineиз управляемых настроек, и без управляемогоstatusLineстрока состояния отключена. Удалите эту настройку или установите её наfalseв файле, который её устанавливает, чтобы повторно включить. См.disableAllHooks. - Если ваша организация устанавливает
allowManagedHooksOnlyв управляемых настройках, ваша пользовательская строка состояния исчезает без предупреждения: вы можете получить строку состояния только из значенияstatusLineв этих управляемых настройках. См. что запускается подallowManagedHooksOnlyдля полного поведения и спросите у вашего администратора, применяется ли эта настройка к вам. - Запустите
claude --debug, чтобы записать код выхода и stderr из первого вызова строки состояния в сеансе - Попросите Claude прочитать ваш файл настроек и выполнить команду
statusLineнапрямую, чтобы выявить ошибки
Строка состояния показывает -- или пустые значения
- Поля могут быть
nullперед завершением первого ответа API - Обрабатывайте нулевые значения в вашем скрипте с резервными значениями, такими как
// 0в jq - Перезагрузите Claude Code, если значения остаются пустыми после нескольких сообщений
Процент контекста показывает неожиданные значения
- Используйте
used_percentageдля простейшего точного состояния контекста - Процент контекста может отличаться от вывода
/contextиз-за времени расчёта каждого
Ссылки OSC 8 не кликабельны
-
Убедитесь, что ваш терминал поддерживает гиперссылки OSC 8 (iTerm2, Kitty, WezTerm)
-
Terminal.app не поддерживает кликабельные ссылки
-
Если текст ссылки отображается, но не кликабелен, Claude Code может не обнаружить поддержку гиперссылок в вашем терминале. Установите переменную окружения
FORCE_HYPERLINKдля переопределения обнаружения перед запуском Claude Code:FORCE_HYPERLINK=1 claudeВ PowerShell сначала установите переменную в текущем сеансе:
$env:FORCE_HYPERLINK = "1"; claude -
Сеансы SSH и tmux могут удалять последовательности OSC в зависимости от конфигурации
-
Если последовательности экранирования отображаются как буквальный текст, например
\e]8;;, используйтеprintf '%b'вместоecho -eдля более надёжной обработки экранирования
Глюки отображения с последовательностями экранирования
- Сложные последовательности экранирования (цвета ANSI, ссылки OSC 8) могут иногда вызывать искажённый вывод, если они перекрываются с другими обновлениями пользовательского интерфейса
- Если вы видите повреждённый текст, попробуйте упростить ваш скрипт до простого текстового вывода
- Многострочные строки состояния с кодами экранирования более подвержены проблемам отображения, чем однострочный простой текст
Требуется доверие рабочей области
- Поскольку
statusLineвыполняет команду оболочки, Claude Code запускает её под тем же правилом доверия рабочей области, что и hooks в файлах настроек. Принятие диалога для папки или для родительского каталога, чьё доверие распространяется на неё, достаточно. - До этого строка состояния остаётся пустой, и
claude --debugзаписываетStatus line command skipped: workspace trust not accepted. Перезагрузите Claude Code и примите диалог доверия, чтобы включить его.
Ошибки скрипта или зависания
- Скрипты, которые выходят с ненулевыми кодами или не выводят результат, вызывают пустую строку состояния
- Медленные скрипты блокируют обновление строки состояния до их завершения. Держите скрипты быстрыми, чтобы избежать устаревшего вывода.
- Если новое обновление срабатывает, пока медленный скрипт работает, выполнение скрипта отменяется
- Протестируйте ваш скрипт независимо с макетными входными данными перед его настройкой
Уведомления делят строку состояния
Вне полноэкранного отображения Claude Code показывает уведомления на той же строке, что и ваша строка состояния. При полноэкранном отображении Claude Code выделяет уведомлениям отдельную строку.
- Системные уведомления, такие как ошибки MCP сервера и автоматические обновления, отображаются на правой стороне строки. Временные уведомления, такие как предупреждение о низком контексте, также циклируют через эту область.
- Включение подробного режима добавляет счётчик токенов в эту область
- На узких терминалах эти уведомления могут обрезать вывод вашей строки состояния