Перейти к содержимому

Интеграция со Starship

Используйте dwe prompt, чтобы выводить компактный, осведомлённый о проекте сегмент внутри промпта Starship.

dwe prompt печатает одну строку для текущего рабочего каталога шелла:

{▪} my-project ✓ ●

Полная форма вывода (каждый хвостовой сегмент независимо опционален):

{▪} <проект> [<сервис>] <иконка-деплоя> <иконка-стека>
  • {▪} — логомарка DWE; внутренний квадрат окрашен токеном accent проекта из workspace/styles.yml.
  • <проект>project.name из workspace.yml, с откатом на basename каталога.
  • [<сервис>] — присутствует, когда cwd находится внутри исходного каталога сервиса. Для каждого workspace/services/<name>/ поле dir: из service.yml разрешается относительно корня проекта и сопоставляется с cwd (на вложенных раскладках выигрывает самое глубокое совпадение; если у потомка нет собственного dir, разворачивается цепочка extends:). Сервисы, чей dir: указывает на корень проекта (dir: .) или за его пределы (dir: .., абсолютные пути вне корня), молча пропускаются. Рамка из скобок остаётся простой, а внутреннее имя очищается от спецсимволов. На каждый вызов промпта приходится один os.ReadDir плюс одно небольшое чтение YAML на сервис — это субмиллисекунды на типичных проектах; файлы service.yml крупнее 64 KB на горячем пути пропускаются.
  • <иконка-деплоя>///, отражает журнал состояния деплоя в .dwe/deploy/state.yml. Опускается, когда состояния деплоя нет.
  • <иконка-стека>//, отражает текущее состояние контейнеров. Опирается на .dwe/prompt-cache.yml (см. Иконка стека). Опускается, когда ни кэш, ни обновление не дали значения.

Стабильность гарантируется только для префикса {▪} <проект> — любой другой хвостовой сегмент может отсутствовать в зависимости от состояния проекта.

Когда шелл находится за пределами любого DWE-проекта, команда завершается с кодом 1 и ничего не печатает — Starship скрывает сегмент через предикат when =.

dwe prompt — это горячий путь для промпта шелла: он полностью обходит cobra, валидацию конфига и lipgloss. Холодный старт на современной машине занимает заметно меньше 50 мс.

Добавьте следующий блок в ~/.config/starship.toml:

[custom.dwe]
command = "dwe prompt"
when = "dwe prompt --check"
format = "[$output]($style) "
style = "bold"
description = "DWE project status"

Предикат when = "dwe prompt --check" — молчаливый и работает только по коду возврата: Starship вызывает его раз на промпт, чтобы решить, показывать ли сегмент. Форма command выполняет собственно рендеринг и выводит окрашенный сегмент в stdout.

dwe prompt окрашивает только логомарку и иконки — скобки, имя проекта, тег сервиса и окружающие пробелы остаются без цвета. Так всё остальное остаётся свободным для style и format Starship — их можно переоформить, не борясь со встроенным ANSI:

[custom.dwe]
command = "dwe prompt"
when = "dwe prompt --check"
format = "via [$output]($style) "
style = "dimmed cyan"

Цветовые escape-последовательности внутри сегмента используют \x1b[39m (только цвет переднего плана по умолчанию) — они не сбрасывают окружающие атрибуты.

Вычисляется в этом порядке приоритетов — выигрывает первое совпадение:

ПорядокУсловиеИконкаЦветовой токен
1деплой status: faileddanger
2деплой status: partialwarning
3есть отложенные изменения (присутствует блок pending)warning
4деплой status: deployedsuccess
5нет состояния / not_deployed / ошибка парсинга(опущена)

Failed и partial идут впереди pending, чтобы промпт первым делом сигнализировал о сломанном состоянии — нужно знать, что что-то не так, прежде чем думать о применении отложенных изменений.

Иконка стека отражает текущее состояние Docker-контейнеров. Она не зависит от иконки деплоя — проект может показывать ✓ ○ (деплой успешен, контейнеры остановлены вручную).

СостояниеГлифЦветовой токенСмысл
runningsuccessвсе ожидаемые контейнеры подняты
partialwarningчасть контейнеров поднята, часть остановлена
stoppedmuted (по умолчанию для промпта #6B7280; переопределяется через colors.muted в workspace/styles.yml)ни один контейнер не работает
(нет)кэша нет, и обновление не дало пригодного значения

Состояние читается из stale-while-revalidate-кэша в .dwe/prompt-cache.yml:

updated_at: 2026-06-03T12:34:56Z # RFC3339 UTC
state: running # running | partial | stopped
  • TTL: 2 минуты. Чтение свежего кэша — это чистый файловый ввод-вывод, без docker ps.
  • Устарел или отсутствует: промпт вызывает docker ps -q --filter label=com.docker.compose.project=<project> с жёстким тайм-аутом 150 мс. Probe применяет переопределения process_env из workspace/docker.yml / docker.local.yml (например, DOCKER_HOST, DOCKER_CONTEXT), поэтому обращается к тому же демону, что и lifecycle-команды. По тайм-ауту или при любой ошибке запись в кэш не выполняется.
  • Потолок доверия к устаревшему значению: 10 минут (5× TTL). Устаревшее закэшированное значение переживает подтверждённый нулевой результат обновления только в пределах потолка; дальше промпт отображает stopped (по-прежнему не записывая кэш), поэтому стек, остановленный в обход dwe, сходится к , а не показывает бесконечно. Неподтверждённый ноль (ошибка docker / тайм-аут) не понижает состояние ни при каком возрасте.
  • Атомарная запись: временный файл + rename в том же каталоге, поэтому параллельные промпты не могут повредить файл.

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

МестоДействие
dwe runпишет running
dwe restart (без аргумента-сервиса)пишет running
dwe restart <service>инвалидирует (удаляет файл кэша)
dwe stop (без --service)пишет stopped
dwe stop <service>инвалидирует
dwe deploy run (без --service)инвалидирует (деплой может оказаться no-op из-за «already up-to-date»)
dwe deploy run --service <n>инвалидирует
dwe reset run (полная остановка проекта)пишет stopped
dwe reset run --service <n>инвалидирует
dwe services enable/disable --applyпишет running (после того как весь план переключения, включая after-хуки, завершён)
dwe status (верхнего уровня)пишет точный агрегированный Health (running/partial/stopped)
dwe status <subcommand>не пишет (область ограничена секцией)
dwe snapshot restore / rollbackинвалидирует (состояние после restore произвольное)
dwe prompt (синхронное обновление)пишет running, если docker ps вернул > 0 — никогда stopped

Собственное синхронное обновление dwe prompt пишет только running. Нулевой результат docker ps невозможно отличить между двумя случаями:

  • стек действительно остановлен, либо
  • фильтр по лейблу неправильный (шаблонизированный docker.yml.project_name промптом не загружается — см. Известные ограничения).

Разрешить обновлению из промпта писать stopped означало бы либо (а) понизить корректный running, оставленный авторитетным источником (lifecycle / status), либо (б) записать неверный stopped в отсутствующий кэш после инвалидации. Закрепление записи stopped только за авторитетными источниками сохраняет честность кэша.

Правило ограничивает запись, а не отображение: когда закэшированное значение старше 10-минутного потолка доверия, подтверждённый нулевой результат обновления отображает иконку как stopped для этого промпта (файл кэша по-прежнему не трогается). В пределах потолка побеждает устаревшее значение — это не даёт нулю от неправильного лейбла «моргать» иконкой здорового стека.

Запись в кэш везде выполняется по принципу best-effort: ни обновление из промпта, ни lifecycle-команды не падают при ошибках ввода-вывода. Кэш — это про наблюдаемость, а не про корректность.

dwe prompt следует спецификации NO_COLOR: если переменная окружения NO_COLOR установлена в любое значение (включая пустую строку), все ANSI-escape-последовательности подавляются и вывод состоит только из обычных символов.

{▪} my-project ✓ ●
  • Автоопределение светлой/тёмной темы: промпт всегда использует тёмный вариант палитры. Большинство терминалов тёмные; поддержку светлых можно добавить позже через COLORFGBG, если будет спрос.
  • Нет флага -c: dwe prompt всегда поднимается вверх от $PWD. Это намеренно — промпт шелла отражает текущий каталог шелла, а не произвольный указатель на проект.
  • Свой бинарник docker: указание binary_docker = podman (или абсолютного пути) в ~/.config/dwe/config (или любое не-docker значение) обходит обновление, инициированное промптом — shared/prompt жёстко зашивает имя бинарника docker на горячем пути. Переопределения process_env (DOCKER_HOST, DOCKER_CONTEXT, …) к probe применяются, так что конфигурации с несколькими демонами работают; зафиксировано только имя бинарника. Lifecycle-команды и dwe status по-прежнему пишут кэш с правильным бинарником, так что при активной работе иконка остаётся точной; вхолостую не выполняется только 2-минутное обновление.
  • Шаблонизированное имя compose-проекта: проекты, у которых workspace/docker.yml задаёт project_name шаблоном (например, ${project.prefix}_${project.name}), при обновлении из промпта получат от docker ps ноль строк — shared/prompt читает только литеральный project_name и для шаблонных значений откатывается на prefix-name. В сочетании с правилом no-downgrade обновление из промпта ничего не пишет — иконка остаётся корректной, пока lifecycle-команды и dwe status (которые знают настоящее compose-имя) держат кэш свежее 10-минутного потолка доверия. За потолком постоянный нулевой результат отображает даже для работающего стека до следующей авторитетной записи; если это мешает, задайте литеральный project_name (например, в workspace/docker.local.yml).
  • Ручной docker stop вне dwe: кэш обновлением из промпта не перезаписывается (правило no-downgrade), поэтому иконка показывает устаревшее состояние до 10-минутного потолка доверия; после него подтверждённый нулевой результат отображает на каждом промпте. Запустите dwe status, чтобы обновить закэшированное состояние немедленно.
  • Сервисы без dir:: tool/infra-сервисы (и любое приложение без примонтированных исходников) никогда не появляются как [<сервис>] в промпте — у них нет каталога исходников, внутри которого мог бы находиться cwd. Проект, деплой и стек промпт при этом рендерит как обычно.
  • Пути через симлинки: dwe prompt не вызывает filepath.EvalSymlinks ни для cwd, ни для разрешённого dir:. Если до cwd добрались через симлинк, а dir: указывает на канонический путь (или наоборот), тег сервиса молча исчезает. Чтобы избежать сюрпризов, используйте в dir: из service.yml реальные пути.
  • Кавычки в разных шеллах: sh, bash и zsh принимают строки command / when как есть. Пользователям fish может потребоваться скорректировать кавычки в starship.toml, если их конфиг Starship оборачивает команды иначе.

Без сегмента:

~/code/my-project ❯

С сегментом (корень проекта):

{▪} my-project ✓ ● ~/code/my-project ❯

С сегментом (внутри исходного каталога сервиса api — при условии, что в workspace/services/api/service.yml задано dir: ./services/api):

{▪} my-project [api] ✓ ● ~/code/my-project/services/api ❯

(Логомарка DWE и иконки окрашены в реальных терминалах.)