21. CLI
CLI archlang покрывает всю поверхность инструментария: проверку и форматирование файлов, запуск проверок governance и CI-гейтов, запись полей модели, экспорт разрешённой модели, headless-рендер диаграмм, обслуживание HTTP API, запуск языкового сервера, проверку привязок-свидетельств по репозиторию кода и проверку соответствия (conformance) развёрнутой системы модели. Двенадцать подкоманд, один бинарник. Эта глава разбирает, что делает каждая, какие флаги она принимает и как встроить их в CI и pre-commit хуки.
Установка
Заголовок раздела «Установка»npm install -g @archlang/cliПосле этого archlang появится в вашем PATH. CLI — это тонкая обёртка над @archlang/engine (парсинг, разрешение, валидация) и @archlang/lsp (общий загрузчик пакетов) — всё, что нужно этим пакетам, уже включено в бинарник. PNG-вывод render идёт через @archlang/render, который включает нативный бинарь resvg и шрифт Inter — единственная подкоманда с нативной зависимостью; всё остальное — чистый JS.
archlang --helparchlang --version--version печатает версию инструментария. --help перечисляет все подкоманды и их флаги.
archlang validate
Заголовок раздела «archlang validate»archlang validate <path>Парсит каждый .arch файл внутри <path> (рекурсивно, с учётом вложенных границ package.archspace), запускает резолвер, запускает валидатор. Печатает диагностики в stdout. Возвращает ненулевой код выхода, если что-то не прошло.
archlang validate . # current directoryarchlang validate examples/demo # a specific packagearchlang validate orders.arch # a single file (its package is auto-detected)Однофайловые черновики — полноправны. Отдельный .arch без окружающего пакета — вполне хороший способ начать: он может зависеть от локально определённых типов и стандартной библиотеки, чего достаточно, чтобы быстро смоделировать реальную идею. CLI валидирует такой файл сам по себе; повысить его до управляемого пакета можно позже. (См. правила отдельных файлов в главе 11 и главе 12.)
Диагностики используют тот же формат, что выдаёт LSP, — то есть ошибка, которую вы видите в редакторе, совпадает с тем, что печатает CLI. Примеры:
orders.arch:14:5 ERROR Required field 'team' is not fulfilled and not droppedcheckout.arch:8:9 ERROR Process step callee 'Payments' resolves to a module, not an interfaceСтрока-сводка считает три различные степени важности — они не взаимозаменяемы:
2 errors, 1 warning, 4 todos, 0 info- Ошибки — это противоречия: модель утверждает то, что не может быть истиной. Исправьте их.
- Предупреждения — нежелательное, но допустимое: запах, а не ложь.
- TODO — это долг черновика, а не баги: упомянутый, но не определённый модуль, интерфейс или подпроцесс, который компилятор синтезировал как заглушку.
TODOозначает недостающую деталь, а не неправильно. Черновое описание «процесс-сначала» порождает их по замыслу; в середине черновика это нормально.
Зрелая модель не должна выходить в свет с открытыми TODO. Флаг --complete (алиас --no-todos) включает гейт слияния с нулевым TODO: при этом прогон завершается кодом 3, если остался хоть один TODO-диагностика, и вместо простого счётчика печатает локализованный чек-лист по категориям того, что осталось.
archlang validate --strict <path> # treat warnings as errors too (exit 2)archlang validate --complete <path> # fail (exit 3) if any TODO remains; prints the punch listarchlang validate --verbose <path> # print every TODO in full, not the grouped summaryРежим наблюдения
Заголовок раздела «Режим наблюдения»archlang validate --watch <path>Перезапускает валидацию при каждом сохранении .arch. Удобно держать терминал открытым рядом с редактором, когда не хочется включать встроенный предпросмотр. Работает до Ctrl-C.
Использование в CI
Заголовок раздела «Использование в CI»Ненулевой код выхода — это контракт. В CI:
archlang validate .Pull-запросы не проходят, пока валидация не пройдёт. Сочетайте с format --check и check ниже.
archlang format
Заголовок раздела «archlang format»archlang format <path>Переписывает файл (или каждый .arch файл внутри каталога) в каноническую форму: нормализованные пробелы, согласованные отступы. По умолчанию он также чеканит стабильный идентификатор для любой декларации без него — именно в форматтере фактически работает правило §3.1 спецификации «пусть инструментарий чеканит его». В остальном форматтер не меняет семантическое содержимое — он только нормализует раскладку.
archlang format --check <path> # exit non-zero if anything would changearchlang format --diff <path> # print the diff that would be applied, but don't writearchlang format --no-mint <path> # skip ID minting; keep bare drafts barearchlang format --indent=2 <path> # spaces per indent level (default 4)archlang format --tabs <path> # use tabs instead of spaces--check — флаг для CI. --diff — локальный флаг «а что бы это сделало?». Ни один из них не пишет на диск.
Если в файле есть ошибки парсинга, форматирование пробелов всё равно выполняется над разбираемыми участками, но чеканка идентификаторов для этого файла пропускается (чеканка записывает постоянный идентификатор — никогда не в исходник, который парсер не может разобрать).
Стабильные идентификаторы
Заголовок раздела «Стабильные идентификаторы»Идентификатор бессмысленен и постоянен — по-настоящему случайный токен, который не меняется, даже когда названная им вещь переименована, переведена в другую область видимости или перемещена. Поскольку случайно набирать руками тяжело, пусть инструментарий чеканит его за вас. Форматтер (и автодополнение редактора) генерирует по-настоящему случайные идентификаторы для любой декларации, которая проходит путь format без идентификатора. Всегда предпочитайте инструмент написанию вручную.
Не редактируйте стабильный идентификатор вручную после записи — никогда не меняйте его и не вшивайте в него имя домена или типа (всё осмысленное рано или поздно устаревает и провоцирует переименование, что сводит на нет весь смысл). Переименования опираются на идентификатор, так что движок диффа видит одну переименованную декларацию вместо удаления-плюс-добавления (Глава 13).
archlang check
Заголовок раздела «archlang check»archlang check <path>Запускает четыре фазы по очереди и отчитывается по всем, даже если более ранняя провалилась, — так вы видите всю картину сразу, а не чините-перезапускаете-чините: validate, затем policy-check, затем format --check, затем evidence. Код выхода — максимум по фазам: 0, если всё чисто, 1, если validate нашёл ошибки, политика подняла активную ошибку, format изменил бы файлы или привязка-свидетельство сломана, 2 под --strict, если найдены только предупреждения.
check — это более тяжёлый собрат validate. Используйте validate во внутреннем редакторском цикле, где нужна быстрая обратная связь; используйте check в CI и на крупных контрольных точках.
archlang check --strict <path>Пробный прогон гейта изменений
Заголовок раздела «Пробный прогон гейта изменений»archlang check <path> --against=mainДобавляет ещё одну фазу: разрешает рабочее пространство и на заданном git-ref (база), и в рабочем дереве (head), затем вычисляет гейты изменений v0.10 when по этому диффу и сообщает, какие гейты сработали и какого ревью каждый потребовал бы. Она также заново вычисляет правило двухстороннего waiver в контексте диффа — waiver, введённый или изменённый в этом изменении, по определению непровверен, так что любая находка, которую он иначе подавил бы, реактивируется в отчёте, даже если представление head-состояния (render/LSP/policy-check) показывает её как waived. Это пробный прогон: CLI не хранит состояние одобрений, так что сработавший гейт просто проваливает проверку (exit 1) — запись и принудительное применение реальных одобрений — задача Studio, а не CLI.
archlang policy-check
Заголовок раздела «archlang policy-check»archlang policy-check <path>Вычисляет каждую декларацию policy в рабочем пространстве по производному графу зависимостей и сообщает об активных (не подавленных waiver’ом) находках. Уровни важности политики:
error— проваливает гейт (exit 1).warning— сообщается; проваливает только под--strict(exit 2).advisory— только для отчёта, никогда не проваливает.
archlang policy-check --strict <path>archlang policy-check --json <path>Политика, использующая ещё не поддерживаемый селектор или выражение, пропускается с пометкой в stderr вместо падения всего прогона — остальной набор политик по-прежнему применяется.
archlang evidence
Заголовок раздела «archlang evidence»archlang evidence <path> [--repo=<path>] [--json]Проверяет каждую привязку-свидетельство sources: в рабочем пространстве по коммиту, закреплённому в package.archspace (Глава 37): указанный путь является blob на этой ревизии, диапазон строк помещается внутрь него, а origin этой рабочей копии совпадает с заявленным репозиторием. --repo указывает на рабочую копию кода; без него используется текущий каталог.
https://github.com/acme/shop @ a1b2c3d ✓ Checkout src/checkout/index.ts:12-88 ✗ Checkout src/checkout/tax.ts:5-40 · Ledger src/ledger.ts
1 verified, 1 broken, 1 unverifiedКод выхода равен 1 только если какая-то привязка сломана. Привязка, которую вообще не удалось проверить — нет репозитория, нет закрепления, не тот репозиторий — сообщается как unverified и не проваливает прогон. Асимметрия намеренна: сломанная привязка это дефект, который автор может починить, а непроверяемая обычно означает, что у машины, где идёт проверка, нет репозитория. При этом unverified по-прежнему никогда не считается успехом, и ничто не показывает его как успех.
Все семь диагностик свидетельств имеют серьёзность warning, поэтому они всплывают в validate и check, не проваливая их; проваливает только код выхода по сломанной привязке.
archlang set
Заголовок раздела «archlang set»archlang set <path> <target> <field> <value> [--aspect]archlang set <path> <target> <text> --descriptionHeadless-запись в модель. <target> адресует модуль по #id, квалифицированному пути (Platform.Auth) или уникальному имени. Запись идёт через тот же слой мутаций, который используют редакторы board/inspector, так что ручное форматирование и комментарии сохраняются, — а запись отказывает (exit 1, с печатью причины), вместо того чтобы когда-либо повредить файл.
archlang set . Payments version 2.1 # field: version: 2.1archlang set . Payments team Core --aspect # aspect: team: Corearchlang set . Payments "Owns payments" --descriptionСоздано для использования в CI/CD — поднять поле version при релизе, проставить окружение деплоя — там, где скрипту нужно тронуть одно поле без ручного редактирования исходника .arch.
archlang export
Заголовок раздела «archlang export»archlang export json <path>Выгружает разрешённую модель рабочего пространства как JSON — та же форма, что у GET /api/model на archlang serve, так что CI-задаче, которой нужна модель, не приходится поднимать сервер.
archlang export json . -o model.json # write to a file instead of stdoutarchlang export json . --stdlib=<path> # override the stdlib lookupПолезная нагрузка — { model: { modules, processes, subprocesses, views, types, documents?, backlinks? }, diagnostics: [...] }.
archlang export html
Заголовок раздела «archlang export html»archlang export html . -o architecture.htmlПишет ОДИН самодостаточный HTML-файл: оболочка Viewer, ваше рабочее пространство и шрифты — всё внутри. Он открывается двойным щелчком, на машине, где ничего нашего никогда не устанавливали, с выдернутой сетью — без сервера, без развёрнутого Viewer, без настройки.
Это артефакт для мест, куда встраивание из главы 23 не дотягивается: приложить к ревью, отправить архитектору письмом, положить в релиз, передать кому-то за пределами вашей сети. Внутри файла — полноценный Viewer: навигация, пространства, представления процессов и таблиц, ваши документы .md (только для чтения — снимок заморожен), вложенные в них картинки, экспорт PNG/SVG; без редактора и сравнения, которые замороженному снимку ни к чему.
archlang export html . -o out.html --force # emit despite error diagnostics-o обязателен (файл — мегабайты разметки). Команда отказывается работать с пространством, где есть диагностики уровня error, пока не передан --force, и отказывается перезаписать один из ваших исходников. --force также разрешает -o писать за пределы текущего каталога — иначе это запрещено.
Текст рисуется вшитым Inter — тем же шрифтом, что и в headless-экспорте PNG. Поэтому артефакт ближе к выводу archlang render, чем к тому, что показывает ваш браузер со своим системным шрифтом.
Это экспорт, а не замена встраиванию: файл — снимок, он не обновляется вслед за моделью. Для живой диаграммы на странице, которой вы управляете, встраивайте Viewer.
archlang render
Заголовок раздела «archlang render»archlang render [path] --out=<file.svg|file.png> [--view=board|bpmn|flow|sequence] ...Headless-экспорт SVG/PNG — без браузера, без сервера. Это тот же конвейер walk→layout→SVG, который используют эндпоинты /api сервера предпросмотра и инструмент MCP arch_render.
archlang render . --view=board --out=board.svg # module board (default view)archlang render . --view=bpmn --process=Orders.Checkout --out=flow.png --scale=3archlang render . --view=flow --process=Orders.Checkout --out=flow.svgarchlang render . --view=bpmn --process=Orders.Checkout --out=diff.svg --diff-base=../old-workspacearchlang render . --view=board --out=compare.svg --diff-base=../old-workspace # Before/Delta/After + чек diff'аarchlang render . --view-name=CheckoutOps --out=ops.svg # a declared flow viewФлаги:
--out=<file>— обязателен и должен заканчиваться на.svgили.png(расширение выбирает формат;--format=svg|pngпереопределяет записываемые байты, но не требование к расширению). Путь обязан разрешаться внутри текущего каталога и не может быть одним из исходников модели.--view=<v>—board(по умолчанию),bpmn,flowилиsequence.bpmn/flow/sequenceотрисовывают процесс и требуют--process.--view-name=<name>— вместо этого отрисовать объявленнуюviewпо имени (её телоflowвыбирает plain/sequence/bpmn, включая раскладку lane/pool bpmn-проекции; экземпляры view связывают свои knob’ы). Взаимоисключим с--processи--diff-base.--process=<name>— квалифицированное точечное имя процесса (например,Orders.Checkout), обязателен дляbpmn/flow/sequence.--expand=all|<ids>— развернуть узлы do-подпроцессов:allлибо список id do-узлов через запятую.--diff-base=<path>— сравнить со старым рабочим пространством по этому пути. С--view=boardотрисовывается артефакт Before / Delta / After (Delta — объединение обеих ревизий, единственная панель, где добавленное и удалённое видно вместе), а в чек добавляется блокcompare: классификация каждого изменения, явная формулировка правил идентичности, сырые и семантические хеши обеих сторон и массивlimitations— что сравнение сказать не может. С--view=bpmnотрисовывается объединённый каркас процесса с акцентами изменений.flow/sequenceдиффа не поддерживают.--scale=<n>— только для PNG: множитель зума для разрешения (по умолчанию 2, ограничен 8).--force— отрисовать, несмотря на диагностики уровня error, и разрешить--outвне текущего каталога. Перезаписать исходник модели он не разрешает никогда.--stdlib=<path>— переопределить путь поиска stdlib.
Растеризация PNG идёт через встроенный resvg пакета @archlang/render — детерминированный вывод, встроенные шрифты Inter, без зависимости от системных шрифтов.
Сначала проверка, потом доставка. Модель с диагностиками уровня error отклоняется (exit 1), а не отрисовывается: в артефакте не хватало бы всего, что не разрешилось, а exit 0 сказал бы CI, что всё в порядке. Артефакт пишется через staging-файл, который затем переименовывается на место, поэтому после отказа или сбоя предыдущий артефакт — последний хороший — остаётся нетронутым. Каждый запуск кладёт рядом <out>.receipt.json с хешем входа, хешем артефакта, счётчиками и перечнем того, чего расписка не утверждает (визуальная проверка не заявляется никогда); при отказе расписка называет хеш сохранённого последнего хорошего артефакта и перечисляет каждую блокирующую диагностику как расписку о ремонте: её зарегистрированный код, msgKey/params, из которых собрано сообщение (включая кандидатов did-you-mean), и supportedFixes — что именно правит починка: вход модели или вход солвера. Диагностика, первопричина которой — другая диагностика в том же файле, отбрасывается, поэтому список называет причины, а не симптомы. Инструмент MCP arch_render (force: true, <outFile>.receipt.json) и маршруты отрисовки /api сервера (?force=1, HTTP 409 с распиской отказа, X-ArchLang-Receipt при успехе) следуют тем же трём правилам.
archlang serve
Заголовок раздела «archlang serve»archlang serve [path] [--port=<n>] [--web-root=<path>] [--quiet]Запускает HTTP API сервер — разрешённую модель плюс REST-эндпоинты и опциональный веб-интерфейс, если --web-root указывает на собранный. Оборачивает CLI @archlang/server; каждый флаг пробрасывается как есть. Порт по умолчанию — 3100; --port=0 выбирает эфемерный порт. Работает бесконечно — для выхода SIGINT/SIGTERM.
archlang lsp
Заголовок раздела «archlang lsp»archlang lspЗапускает языковой сервер на stdio для клиентов редактора/IDE, которые хотят запускать LSP как подпроцесс, а не полагаться на встроенную копию. VS Code и IntelliJ включают LSP напрямую (см. Главу 22); archlang lsp — для построения собственной интеграции. Работает бесконечно, пока клиент не пришлёт уведомление LSP exit или процесс не получит SIGINT/SIGTERM.
archlang conform
Заголовок раздела «archlang conform»archlang conform <path> --source=jaeger|tempo --endpoint=<url>Проверяет развёрнутую систему по модели с помощью трасс OpenTelemetry — направление «модель-к-реальности», дополняющее направление validate «синтаксис-к-модели». Два измерения: соответствие графа сервисов (агрегированные рёбра, видимые в трассах, против рёбер, объявленных моделью) и соответствие процесса по трассе (совпадают ли шаги наблюдаемой трассы с шагами смоделированного процесса, по порядку).
archlang conform . --source=jaeger --endpoint=https://jaeger.internal \ --metrics-endpoint=https://prom.internal --lookback=3600 --limit=1000 --json--source=jaeger|tempoи--endpoint=<url>обязательны.--metrics-endpoint=<url>— опциональный бэкенд метрик для более богатого сигнала соответствия.--lookback=<sec>— окно запроса трасс (по умолчанию 3600, ограничено неделей).--limit=<n>— максимум трасс для выборки (по умолчанию 1000, ограничено 100 000).- Авторизация: задайте
ARCHLANG_OTEL_TOKENв окружении; он отправляется как заголовокAuthorizationбэкенда и никогда не логируется и не отражается.
Exit 1 при любом нарушении соответствия (рёбра, видимые только в телеметрии и не объявленные моделью, неизвестные трассы, лишние/пропущенные/переставленные шаги) или если бэкенд вообще не вернул данных.
archlang info
Заголовок раздела «archlang info»archlang info <path>Печатает сводку пакета: имя, версия, число файлов/модулей/типов/процессов/проекций/документов, объявленные пространства (spaces) и дерево транзитивных зависимостей.
Сценарии использования:
- Введение в проект. Новый член команды запускает
archlang infoна пакете, в котором ему предстоит работать; он получает обзор размером в один экран, не открывая файлы. - Артефакты CI. Сохраняйте вывод как артефакт сборки, чтобы рецензенты PR могли с одного взгляда увидеть, как выглядит архитектура ветки.
- Аудит миграций. Запускайте на старом пакете при его моделировании, затем перезапускайте после каждого прохода рефакторинга.
Комбинирование команд
Заголовок раздела «Комбинирование команд»Типичный pre-commit хук:
#!/usr/bin/env bashset -earchlang format --check .archlang validate .Типичная CI-задача:
archlang format --check .archlang check . --against=origin/mainarchlang info . > arch-summary.txtformat --check гарантирует, что каждый файл канонически отформатирован. check --against запускает validate + policy-check + format-check + evidence плюс пробный прогон гейта изменений относительно базовой ветки PR. info выдаёт артефакт для рецензентов.
Коды выхода
Заголовок раздела «Коды выхода»Для целей CI считайте провалом любой ненулевой код выхода.
0— успех, ошибок нет.1— диагностики уровня error, активная ошибка политики, несоответствие формата под--check, сломанная привязка-свидетельство, нарушение соответствия (conformance) или сбой загрузки.2— под--strictи хотя бы одна диагностика/находка уровня warning (validate,check,policy-check).3— под--completeи остался хотя бы один TODO (толькоvalidate).64— ошибка использования: неизвестная подкоманда, отсутствует обязательный аргумент.
validate --watch и serve работают до прерывания; их отражаемый код выхода (0) отражает чистое завершение через SIGINT/SIGTERM, а не результат валидации.
Чего нет в виде отдельной подкоманды
Заголовок раздела «Чего нет в виде отдельной подкоманды»Дифф диаграмм не отдельная подкоманда — это два флага на командах выше: render --diff-base=<path> отрисовывает визуальный дифф (доска Before/Delta/After или объединённый каркас BPMN с --view=bpmn), а check --against=<ref> запускает дифф гейта изменений относительно git-ref. Базовый дифф — это ещё и библиотечный API (BpmnEdge.diff и соседи из @archlang/engine), который режим диффа Viewer потребляет напрямую.
Системы плагинов нет — всё, что делает CLI, — это одна из двенадцати подкоманд выше; настройка происходит через прямое использование библиотечных пакетов (@archlang/engine, @archlang/viewer-core, @archlang/render).
- Двенадцать подкоманд:
validate,format,check,policy-check,evidence,set,export,render,serve,lsp,conform,info. validate— для внутреннего цикла;check(опционально--against=<ref>) — для CI;policy-check— для governance;info/export— для сводок и инструментария.setheadless-пишет одно поле/аспект/описание;renderпроизводит headless SVG/PNG;serveзапускает HTTP API;lspзапускает языковой сервер;conformпроверяет телеметрию живой системы по модели.- Коды выхода стабильны; конвейеры ориентируются на них.
- Нет системы плагинов, нет отдельной подкоманды diff — дифф живёт в
render --diff-base/check --againstи в библиотечном diff API движка.
Что дальше
Заголовок раздела «Что дальше»Глава 22: Интеграция с редактором → — что языковой сервер даёт в VS Code и JetBrains.