Перейти к содержимому
XBSL (1C:Element)
Русский
Esc
↑↓navigate↵open⌘Jpreview
На этой странице

Серверы и плагины

Сервер LSP для редакторов, сервер MCP для агентов, локальная веб-панель и плагины со своими правилами, данными и уровнями.

Движок один, а обратиться к нему можно по-разному: резидентным сервером для редактора, набором инструментов для агента, страницей в браузере. Есть и способ расширить всё это разом.

LSP-сервер

xbsl-lsp из набора [lsp] (pip install "xbsl[lsp]") запускает линтер долгоживущим Language Server по stdio. Пофайловые диагностики он даёт на лету, при наборе, а проектные – по сохранению. Переход к определению, автодополнение и подсказку при наведении он строит по резидентному индексу проекта, а быстрые исправления предлагает прямо в редакторе. Платить за старт интерпретатора на каждый вызов при этом не нужно. Ключи: --project-root (корень исходников относительно папки рабочей области), --select/--ignore/--enable, --data-dir, --baseline, --templates. Подойдёт сервер любому редактору с поддержкой LSP – VS Code, Neovim, JetBrains.

С --project-root сервер проверяет и словарь перевода проекта. Другие yaml-файлы вне корня он не проверяет.

Проверка всего проекта читает только корень и словарь. Любой другой открытый файл, например модуль вне корня, сохраняет находки своей проверки, пока его не закроют.

Сервер живет столько же, сколько окно редактора, и код и данные на диске могут смениться под ним: self-update, git pull редактируемой установки, обновление надстройки, переустановка, которая переписывает данные платформы. Проверяет он при этом тем, что загрузил при старте, и его находки расходятся с CLI и CI. Поэтому сервер сверяет версию движка, установленные надстройки, свои файлы кода и прочитанные файлы данных платформы с их состоянием при запуске и, если что-то из этого изменилось, один раз на каждое изменение показывает предупреждение с просьбой перезапуска (в VS Code – команда “XBSL: Перезапустить линтер”). Проверки продолжаются, сам сервер не перезапускается.

Всё, что редактору нужно для кода, – это стандартный LSP, поэтому обычный клиент работает без дополнительной обвязки. Сверх этого сервер отвечает на собственные запросы xbsl/*. На них построены панели расширения VS Code, и по ним же эти панели можно повторить в другом редакторе:

xbsl/reindexProject дожидается свежего полного индекса и проверки проекта. После публикации диагностик возвращаются {ok, files, diagnostics}, при ошибке также error. Ожидающий запуск по таймеру отменяется; активная проверка завершается перед новой. Открытые измененные буферы сохраняют свою текущую диагностику.

Необязательный workDoneToken получает стандартные уведомления LSP $/progress со значениями begin, report и end. Локализованные сообщения называют восемь этапов: ожидание, индекс, чтение исходников, файловые правила, правила проекта, словарь перевода, публикация диагностик и завершение. Индексация, чтение, файловые правила и проверка словаря считают обработанные и оставшиеся файлы; проверка проекта – выполненные и оставшиеся правила с названием текущего; публикация – документы, для которых отправлены диагностики. Проценты в сообщении относятся к текущему этапу. Стандартное поле percentage достигает 100 только после успешной публикации. Ошибка или отмена завершают прогресс без сообщения о полном выполнении.

Группа Запросы
Диагностики и подсказки xbsl/relint, xbsl/reindexProject, xbsl/hoverDoc, xbsl/templatesReload
Документация платформы xbsl/docsAvailable, xbsl/docsSearch, xbsl/docsPage, xbsl/docsTree, xbsl/docsAsset, xbsl/docsForSymbol, xbsl/docsByName
Схемы и словари xbsl/uiSchema, xbsl/metadataSchema, xbsl/formKeys, xbsl/metaKeys, xbsl/metaCapabilities, xbsl/httpMethods
Создание метаданных xbsl/objectInfo, xbsl/metaNewObject, xbsl/metaAddField, xbsl/metaSetFieldProperty, xbsl/metaAddForm, xbsl/metaAddRoute, xbsl/metaAddSubsystem, xbsl/metaProjectInfo, xbsl/metaMoveObject, xbsl/metaDeleteObject, xbsl/metaRenamePackage, xbsl/metaMoveResource, xbsl/metaRenameResourceFolder, xbsl/metaDeleteResourceFolder, xbsl/metaResourceReferences, xbsl/metaAddLocalization, xbsl/localizationInfo
Формы xbsl/formTree, xbsl/formNodeAt, xbsl/formEdit, xbsl/searchForms, xbsl/bindingComplete
Обработчики событий xbsl/moduleHandlers, xbsl/addHandler, xbsl/addModuleMethod, xbsl/removeHandler

Запрос на создание метаданных возвращает план: полный текст каждого файла, который был бы записан. Редактор применяет этот план одной отменяемой правкой, а сам сервер не пишет ничего. CLI и MCP-сервер на том же коде пишут. xbsl/metaCapabilities отвечает версией сервера и наборами видов, которые он умеет создавать: объектов, элементов разделов, форм. По ним клиент строит меню от живого движка, а не от жёстко зашитого списка.

MCP-сервер

Тонкий адаптер над тем же ядром. Агент – например, Claude Code – зовёт проверки как инструменты и получает структурированные диагностики.

pip install -e ".[mcp]"
claude mcp add xbsl -- xbsl-mcp

Каждый пишущий инструмент meta_* применяет изменения и в том же ответе возвращает проверку записанных файлов: создание и проверка за один вызов. Проверка краткая: число файлов и находок и до десяти находок по строке на каждую. Полный отчёт по тем же файлам даёт lint_paths. Ядру и CLI зависимость mcp не нужна, она лежит только в наборе [mcp].

Когда клиент сообщает, что соединение закрыто. Сервер ведёт короткий журнал в локальной папке пользователя: когда он запустился, как завершился и что его остановило. xbsl self-update --stop-holders останавливает серверы всех сессий, чтобы заменить пакет, и клиент такого сервера видит только закрытое соединение. xbsl mcp-log печатает журнал и называет причину. Перезапуск MCP-клиента поднимает новый сервер. Журнал лежит в %LOCALAPPDATA%\xbsl\mcp-journal.jsonl в Windows и в $XDG_STATE_HOME/xbsl/mcp-journal.jsonl в других системах; переменная XBSL_MCP_JOURNAL задает другой файл.

Когда движок на диске обновили под работающим сервером. Часть модулей сервер загружает, только когда они понадобятся инструменту. После self-update или git pull редактируемой установки такие модули приходят из нового кода, а загруженные остаются старыми, и половины не сходятся. Перед каждым вызовом сервер читает версию из __init__.py на диске и проверяет, не изменились ли с запуска файлы кода движка: git pull между двумя выпусками меняет их, а номер оставляет прежним. Для этой проверки сервер смотрит на несколько папок, где лежат файлы, а сами файлы перечитывает, только когда папка изменилась – git заменяет файл, удаляя его и записывая заново, – или когда с прошлого чтения прошло пять секунд: редактор может переписать файл на месте. Если изменилась версия или файлы, все инструменты, кроме version_info, не выполняются: вместо этого они отвечают полем error, которое называет изменение и просит перезапустить сервер, и записью stale. У записи причина version с обеими версиями или причина sources: версия с обеих сторон одна, а fingerprint – отпечаток файлов на диске, по нему одно изменение отличают от следующего; ran: false говорит, что инструмент не выполнялся. version_info отвечает как обычно, показывает engine_on_disk и несет ту же запись stale. Код надстроек сверяет их собственная проверка (ниже). Если инструмент все же упал, сервер сверяет все файлы кода, вместе с кодом надстроек, с их состоянием при запуске и, если они изменились, тоже просит перезапуск; тогда запись несет ran: true: до сбоя инструмент мог что-то записать. Упавшее правило говорит то же в своей находке. Сам сервер не перезапускается и не завершается (это делает за него супервизор), а xbsl mcp-log показывает, когда он впервые заметил каждое изменение.

Когда данные платформы изменились под работающим сервером. Некоторые модули берут константы из пар терминов платформы, пока сервер их импортирует, и сброс кэшей их не перестраивает. Сервер, который запустился за мгновение до того, как переустановка записала новые файлы данных, так и отвечал по старым парам, а CLI читал новые. Поэтому сервер запоминает размер и время изменения каждого файла данных, который читает, и перед каждым вызовом сверяет их с диском. Проверка обращается к файловой системе один раз на файл и сами файлы не читает. Если прочитанный файл изменился или пропал либо появился файл, который сервер искал и не нашел, инструменты отказывают так же, с причиной data. Сюда относится и корень данных надстройки, у которого index.json появился после запуска. Запись перечисляет файлы в changed относительно root, у каждого пометка modified, added или removed. loaded и on_disk называют версии данных, а fingerprint отличает одно изменение от следующего. version_info несет ту же запись. Файл, которого сервер не читал, не считается: ни один ответ не опирался на него. Новая версия данных все равно заметна: каждый сервер читает index.json, чтобы выбрать версию. Не сверяется и база документации: каждый вызов открывает ее заново.

Перезапустить сервер может только клиент, а агент, который вызывает инструменты, этого не может. Поэтому отказ инструмента, у которого есть команда CLI, несет поле cli – командную строку того же вызова для POSIX-оболочки (в Windows – Git Bash). Команда запускает тот же интерпретатор, что и сервер: он загружает движок из того же места, выполняет код и читает данные, которые сейчас на диске. Такая команда есть у lint_paths, lint_source, baseline_prune, list_rules, meta_fold_comments, инструментов translate_* и у инструментов чтения meta_project_info, meta_object_info, meta_localization_info, meta_component_tree, meta_resource_references и meta_unused_resources. Команда инструмента чтения печатает те же данные, что и он сам, только без root и file, которые инструмент повторяет в ответе. Текст вызова lint_source и правки, переданные в вызове translate_set, сохраняются во временный файл, команда читает их оттуда, а cli_note называет этот файл; папку таких файлов старше суток, оставшуюся от прежнего сервера, убирает следующий. Относительное имя filename у lint_source попадает в команду уже отсчитанным от рабочего каталога сервера: оттуда его читает инструмент, и там же правила ищут рядом с ним парный yaml, а команда может запуститься из другой папки. Находки команды несут этот абсолютный путь. Интерпретатор запускается с флагом -P, поэтому папка xbsl в текущем каталоге оболочки не подменяет движок. В Python 3.10 такого флага нет, и там каждый запуск команды сначала переходит в эту временную папку, в подоболочке: (cd ПАПКА && ...). Остальные инструменты отказывают, как раньше.

Сверяются и надстройки. Если установленные надстройки не те, что загрузил сервер, инструменты все равно выполняются, на правилах, загруженных при старте, а каждый ответ-объект начинается с stale: причина plugins, изменившиеся дистрибутивы changed с обеими версиями и просьба перезапустить сервер; у инструмента с командой CLI запись несет и cli – ответ по надстройкам с диска. version_info показывает plugins_on_disk рядом с plugins. Заново перечислять установленные дистрибутивы сервер берется, только когда меняется папка, куда их ставят, поэтому вызову хватает одной проверки этой папки.

У каждого инструмента meta_* и у lint_paths есть параметр root – корень проекта вызывающего. Агент в git worktree не делит рабочий каталог с сервером, поэтому параметр и нужен. Относительные directory, yaml_path, module_path, paths, baseline и compare считаются от него, а ответ несёт абсолютные пути записанных файлов и находок и поле root, от которого их считали. Без параметра берётся рабочий каталог сервера, как раньше. Относительный путь из рабочего дерева тогда называет другую рабочую копию, и её чистый ответ выглядит как ваш.

Проверка и окружение

Инструмент Что делает
lint_paths(paths, select, ignore, enable, baseline, no_baseline, root, as_ci, as_ci_job, compact, fix, as_ci_full, compare, list_info) проверить файлы и каталоги на диске. Относительные пути считаются от root, и сводка его называет. Пути нескольких корней проекта (два рабочих дерева одного проекта – это два корня) не сливаются в один проект: каждый корень проверяется так, как его проверил бы отдельный вызов, – со своим окружением проекта, своим списком принятых замечаний и, с as_ci, задачей конвейера своей рабочей копии. Ответ считает находки всех корней, а summary.projects держит запись на каждый корень: project, его собственные счетчики и его записи о списке принятых, задаче конвейера и правках. Список принятых замечаний .xbsllint-baseline применяется сам, как и в CLI: summary.baselined говорит, сколько находок погашено, no_baseline показывает и замороженные, а устаревшие записи названы поименно в summary.baseline_stale_entries, не только сосчитаны. enable добавляет к умолчаниям правило, выключенное по умолчанию, – так проект спрашивает про пробелы словаря. as_ci берет набор правил и список принятых из задачи конвейера проекта (.gitlab-ci.yml или рабочий процесс GitHub рядом с проектом): предварительная проверка тогда судит тем же, чем задача конвейера, а сводка несет as_ci: файл и задачу, включение, в котором стоит команда, корень рабочей копии, сам набор данными (select, ignore, enable, baseline) и одной строкой (flags), прочие задачи с линтером и непрочитанные включения. Той же записью отвечает --format json в командной строке. as_ci_job называет, какую из них взять: конвейер, проверяющий второе дерево с переводом, запускает линтер дважды и двумя разными наборами. Сводка считает находки по правилам, файлам и уровням (by_rule, by_file, by_severity), а compact сужает ответ: сводка остается вместе с записями о списке принятых замечаний и о задаче конвейера, в errors лежат полные записи находок уровня error, а до 10 находок (report.COMPACT_FINDINGS_LIMIT) findings перечисляет их строками файл:строка правило – сообщение. Больше 10 – findings пропадает, а findings_hint говорит, сколько находок и как их прочитать: полный текст каждой стоит по несколько сотен знаков, а вопрос часто один – чисто ли дерево. Находки уровня info сосчитаны, но не перечислены и в 10 не входят: info_hint называет их число и правила, а list_info=true перечисляет их. compact=true также убирает summary.by_file и сводит summary.as_ci к одной строке brief: файл относительно корня рабочей копии, задача, ключи, где длинный список сосчитан (--enable ×10), другие задачи, пока задачу не назвали, и непрочитанные включения. Всю запись о задаче конвейера оставляет as_ci_full=true, а полную карту файлов дает compact=false. Сколько бы проверка ни нашла, краткий ответ укладывается в 12 000 знаков JSON (report.COMPACT_ANSWER_LIMIT): сверх этого списки, которые растут с проверкой, обрезаются с конца – сначала устаревшие и переформулированные записи списка принятых, затем findings, записи errors, записи корней проекта и by_rule. truncated называет каждый обрезанный список ({shown, total}), а truncated_hint говорит, как получить полный ответ. Счетчики не обрезаются никогда. fix=true применяет доступные правки и возвращает оставшиеся находки; по умолчанию инструмент только читает. Принятые места защищены между проходами, файл списка не меняется. summary.fixed считает примененные правки, summary.files_changed – измененные файлы. compare называет файл, где запуск хранится до следующего вызова; файл тот же, что у ключа --compare в командной строке. Первый вызов сохраняет запуск и отвечает compare: {file, compared: false}. Каждый следующий сравнивает запуск с сохраненным, записывает его на место старого и вместо находок отвечает {summary, compare}. appeared и disappeared считают изменившиеся находки, в rules по строке на каждое изменившееся правило (rule, files, findings, appeared, disappeared), findings перечисляет изменившиеся находки записями (path, line, col, rule, message), а not_compared называет пути и правила, которые проверял только один из двух запусков. Пути сопоставляются по каталогу, который они называют, а затем по написанию, поэтому запуски одного каталога по относительному и абсолютному пути сравниваются друг с другом. С compact при числе изменений больше 10 список заменяет подсказка findings_hint, а весь список до следующего вызова лежит в файле, в разделе changes.
lint_source(filename, content, select, ignore) проверить содержимое в памяти, до записи файла
baseline_prune(paths, select, ignore, enable, baseline, dry_run, root) снять записи, которые этой проверке больше не нужны (ключ CLI --prune-baseline). Ответ называет каждую: путь, правило, сообщение, количество и причину, написанную человеком. Файл сохраняет свой порядок и формат. Записи правил, которых у сервера нет, и файлов вне paths не трогаются, а dry_run показывает, что ушло бы. Пути нескольких корней проекта проверяются каждый отдельно; корни со своими списками (два рабочих дерева) чистятся каждый своим вызовом, а вызов по ним сразу отклоняется с названными файлами
list_rules(select, ignore, filter) доступные здесь правила: id, заголовок, тир, область, severity – и params у правила, которое судит по числу (действующее значение, умолчание, переменная среды для переопределения); select отвечает про одно правило вместо всего реестра, а filter сужает ещё: слово, совпадающее с именем группы (частью id до /), выводит только эту группу, а любое другое ищется как подстрока id или слово названия либо описания: это весь текст i18n, зарегистрированный под id правила (название и шаблоны сообщений, из которых собираются диагностики), на любом из языков, плюс английский докстринг; docs/RULES.md не читается – он не бандлится ни с sdist, ни с wheel. Сравнение без учёта регистра. Фильтр, ничего не нашедший, отвечает {error, near_groups} – ближайшими по написанию группами – вместо пустого списка
version_info() чем отвечает окружение: движок, интерпретатор, версия данных, надстройки. Так различают два окружения, которые отвечают на одном файле по-разному. engine_on_disk и plugins_on_disk говорят, что установлено сейчас, а stale появляется, когда это расходится с загруженным или когда прочитанный сервером файл данных платформы изменился на диске (причина data)

Документация платформы и схемы

Инструмент Что делает
docs_search(query, limit) полнотекстовый поиск по документации 1С:Элемент
docs_page(id, brief, section) страница документации по идентификатору из двух других инструментов; brief – одна шапка: краткое описание и имена разделов вместо текста, section – шапка плюс один раздел статьи (Свойства, Методы, Конструкторы, …; на неизвестный раздел ответ – перечень имён на выбор)
docs_symbol(name, brief, section) документация символа по имени, в любом написании: тип отвечает своей страницей и теми же режимами brief и section, член (Подстрока, Строка.Найти) – блоком одного этого члена со склеенными перегрузками: своей страницы у члена нет. Член, объявленный несколькими типами, отвечает их именами и тем, как переспросить
type_members(name) члены stdlib-типа одним компактным ответом – что может стоять после точки; дешевле страницы, когда нужен только список членов
ui_schema(component, brief, property) ui-схема компонента интерфейса: палитра конструктора и типизированные свойства
metadata_schema(kind, sections, names) какие свойства вправе объявить элемент заданного ВидЭлемента

Трём инструментам docs_* нужна база docs.sqlite (см. Поиск по документации), а двум схемным – сгенерированные данные о языке. Страница типа – это тысячи знаков: конструкторы, каждое свойство, списки унаследованного. Целиком она нужна тогда, когда её собираются читать. brief отвечает на вопрос “что это за страница и о чём она”, а section – на один вопрос по ней.

Перевод исходников (см. Перевод проекта)

Инструмент Что делает
translate_status(root, against, full) покрытие и остаток, дешёвая проверка перед любым решением. Корень без словаря получает отказ с перечнем мест, где словарь искали; against называет git-ссылку и добавляет collisions – ключи, которые файлы словаря в рабочем дереве и на ссылке переводят по-разному или одинаково, тот же отчёт, что у xbsl translate --check-duplicates. Дубли, которые есть и на ссылке, приходят одинаковыми при каждом вызове в ветке, поэтому они только посчитаны (duplicates_total, duplicates_at_ref); весь список отдает full=true. Конфликты перечисляются всегда. drift считает фразы, которые перечисляет translate_drift
translate_gaps(root, kind, filter, limit, offset, compact) чего словарь ещё не покрывает, страницами: частота, первые места, собственное написание платформы подсказкой; compact оставляет в строке только ключ, вид и частоту; в ответе – прочитанный dictionary
translate_entries(root, kind, filter, limit, offset, compact) что словарь уже говорит, с файлом и строкой каждой записи; по умолчанию страница в десять строк, а compact оставляет в строке только ключ, вид и значение
translate_unused(root, kind, filter, since, limit, offset, prune, compact, budget_seconds) записи словаря, которые проект больше не использует; filter принимает подстроку или список, since ограничивает кандидатов одной правкой. Просмотр по умолчанию возвращает полные строки; compact=true оставляет ключ, вид, файл и строку. prune=true снимает все ключи, которые выбрали отборы, какой бы ни была страница, вместе со всеми их объявлениями, и обычно не возвращает список: removed считает вхождения, pruned.keys – пары, pruned.by_kind / pruned.by_file дают разбивку. Явное compact=false возвращает полные строки после снятия. counts охватывает весь отфильтрованный набор кандидатов, и prune тоже: страница определяет только список. budget_seconds (по умолчанию 300) ограничивает обход; ответ partial перечисляет кандидатов и ничего не удаляет.
translate_set(root, edits, edits_file, target, comment) запись: добавить, исправить на месте (во всех местах, где объявлен ключ) или снять, обнулив значение; edits_file – пачка файлом (yaml формата словаря, который читает загрузчик самого словаря, поэтому годится и выгрузка с ключами в кавычках, либо список JSON; пачку без записей инструмент отвергает и называет ключи верхнего уровня, которые в ней есть), comment – заголовок, с которым создаётся новый файл словаря. Каждый план записывается тем написанием, какое читает его проход: запись, которая не сработала бы, возвращается в refused, а та, что читается однозначно – фраза с кавычкой, экранированной по правилам литерала, – записывается этим чтением и попадает в normalized

Все четыре отвечают страницами поверх одного ядра движка, поэтому наполнение словаря на тысячи записей не требует вычитывать файлы.

Проект и его объекты

Инструмент Что делает
meta_project_info(root, kind, subsystem, brief, package, project, reference) карта исходников под корнем: проекты, подсистемы, пакеты, объекты по видам; kind, subsystem и package сужают перечень объектов, project обходит только названный проект, а справочные разделы (виды объектов, виды секций, методы доступа) приходят с reference или brief
meta_object_info(root, name, yaml_path) описание одного объекта: всё, что нужно, чтобы писать его формы и код
meta_new_project(...) создать проект: Проект.yaml, Проект.xbsl и первую подсистему
meta_new_object(directory, kind, name, ...) создать объект: <Имя>.yaml и <Имя>.xbsl у видов с модулем; presentation и object_presentation пишут заголовки списка и объекта справочника или документа, record_presentation – заголовок записи регистра сведений или периодического набора констант, periodicity делает набор констант периодическим
meta_rename_object(..., dry_run, full) переименовать объект и обновить все ссылки в исходниках. Ответ краткий: переименованные файлы, заметка со счётом и outside_projects – правленые файлы, которые не принадлежат ни одному проекту, например словарь перевода рядом с проектом. full=true перечисляет каждый правленый файл
meta_delete_object(..., dry_run) удалить объект целиком: пару yaml/модуль, его формы и описания WSDL клиента SOAP-сервиса
meta_move_object(root, yaml_path, target_dir, dry_run) перенести объект с формами, модулями, строкой и таблицей списка в пакет, другой пакет, корень подсистемы или другую подсистему; импорты, нужные ссылкам на новом месте, квалифицированные имена старого места и Использование чинят правила импорта, прогнанные до и после переноса, а занятое имя и непубличный элемент, который станет нужен другой подсистеме, получают отказ
meta_rename_package(root, package_dir, new_name, dry_run) переименовать каталог пакета со всеми файлами и переписать импорт Подсистема::Пакет, элементы Импорт и квалифицированные имена по проекту
meta_move_resource(root, resource_path, target_dir, dry_run) перенести файл ресурса или папку в другую папку того же каталога Ресурсы и переписать ключи Ресурс{...} и значения свойств-картинок, которые на него указывают; обращения по строке – в notes, перенос в другой каталог Ресурсы запрещён
meta_rename_resource_folder(root, folder_dir, new_name, dry_run) переименовать папку внутри каталога Ресурсы со всеми файлами и переписать ключи, которые на них указывают
meta_delete_resource_folder(root, folder_dir, dry_run) удалить папку внутри каталога Ресурсы с файлами и перечислить ключи и обращения по строке, которые на них указывают; dry_run по умолчанию истина
meta_resource_references(root, resource_path, limit) найти места, которые называют файл ресурса или папку: ключи Ресурс{...} и значения свойств-картинок, ключи из двух каталогов, строки с путем или с ключом файла без расширения; у места есть файл, диапазон, строка и вид; limit (по умолчанию 100) ограничивает список, total считает все места, а hasMore равно true, когда в список попали не все
meta_unused_resources(root, include_protected, limit) найти ресурсы без известных обращений; вычисляемые и неопределенные обращения учитываются отдельно, кандидаты требуют проверки, файлы не удаляются
meta_add_subsystem(parent_dir, name, ...) создать подсистему – папку с Подсистема.yaml
meta_add_dependency(root, vendor, name, version, ...) подключить библиотеку – раздел Библиотеки в Проект.yaml
meta_set_access(root, ..., default, permissions, calc_by) задать КонтрольДоступа.Разрешения у объекта

Поля, маршруты, методы, формы, локализация

Инструмент Что делает
meta_add_field(yaml_path, field_kind, name, type, props, names, doc, ...) добавить элемент раздела: реквизит, измерение, ресурс, значение перечисления, параметр, поле, свойство контракта или компонента интерфейса (имя и тип, без Ид), табличную часть. doc – описание элемента, оно пишется документирующим комментарием: строками ## в начале элемента, которые среда разработки читает и сохраняет; стандартному реквизиту, ключу локализованных строк и пачке из нескольких имен описание не задается. names добавляет за один вызов несколько элементов одного вида с общими типом и свойствами, например значения перечисления; пачка планируется целиком, и занятое имя отменяет ее всю. Стандартный реквизит (Номер, Дата, Код, Наименование, Владелец) судится своим классом: Длина, Уникальность и блок Автонумерация принимаются, а type можно не указывать там, где класс его задает. props принимает вложенный блок словарем или ключом через точку (Автонумерация.Префикс), а список – последовательностью. Блок класса, которого метамодель не описывает (Представление), отвергается как известное ограничение. Секция, которой в файле нет, заводится в конце файла (Свойства компонента – перед его События, в порядке, в котором пишет среда разработки); у регистра поле notes говорит об этом и называет соседний вид, который положил бы элемент рядом с существующими полями: ресурс там, где просили реквизит, и наоборот
meta_set_field_property(yaml_path, field_kind, name, props, ...) задать свойства уже существующему элементу раздела; формы значений те же, что у meta_add_field, вложенный блок заменяется целиком, а Тип читается так же, как его type: экранирование разметки снимается, имена платформы пишутся на языке файла
meta_add_route(yaml_path, routes, template, methods) добавить HttpСервис шаблоны url и заготовки обработчиков
meta_add_method(module_path, name, params, returns, ...) вставить метод в модуль .xbsl, не разрывая блоки аннотаций
meta_add_form(root, ..., forms, card_min_width, card_placeholder) сгенерировать формы объекта и зарегистрировать их в Интерфейс
meta_add_localization(yaml_path, language) добавить файл перевода элементу локализованных строк
meta_set_localization(yaml_path, name, values, entries, section, dry_run, full_text) записать одну строку локализации или сразу много во все языки: текст языка по умолчанию – в сам элемент, каждый другой – в свой файл перевода; язык, о котором ключ молчит, всё равно получает строку значением языка по умолчанию и заметкой. entries пишет много ключей ({ключ: values}) за один проход: файл, который трогают несколько ключей, читается и пишется один раз, а весь пакет применяется целиком или не применяется вовсе. dry_run отвечает сводкой summary (ключ, язык, файл, значение до и после) вместо целых файлов; full_text добавляет файлы рядом с ней
meta_localization_info(yaml_path) картина локализации: объявленные языки и что ещё не переведено

Компоненты формы – конструктор из скрипта

Инструмент Что делает
meta_component_tree(yaml_path, node_id, name, max_depth, properties, brief) дерево узлов компонента интерфейса; большую форму можно брать частями – поддеревом (по идентификатору узла или по его Имя), с пределом глубины, без записей свойств либо одним скелетом (brief – идентификаторы, виды, типы, имена и слоты: несколько килобайт на дерево в сотни) – большое целое дерево несёт подсказку об этих ручках
meta_add_component(yaml_path, parent_id, slot, ...) вставить новый компонент в слот родительского узла
meta_insert_fragment(yaml_path, parent_id, slot, fragment, ...) вставить в слот готовый блок yaml одного компонента (скопированное поддерево). Пояснение # из фрагмента ложится туда, где его читает среда разработки: комментарий над компонентом уходит внутрь узла строками ##, а пояснение без такого места остаётся и называется в notes, см. Комментарии в yaml
meta_fold_comments(paths, dry_run, take_proposed, root, compact) свернуть комментарии, которые среда разработки не читает, в описание ближайшего узла, у которого есть место, – то же, что делает xbsl fold-comments, см. Комментарии в yaml. Ответ – отчет --format json: переносы по каждому файлу, итог проверки, число записанных файлов и счет переносов по виду и действию. dry_run по умолчанию истинно, и ничего не пишется; dry_run=false записывает файлы, прошедшие проверку, а take_proposed применяет и спорные переносы, как --all. По целому дереву отчет занимает сотни килобайт, и compact дает краткий: счет, файлы с наибольшим числом переносов и переносы, на которые стоит взглянуть (спорные и оставленные первыми), до десяти строк каждый список, и все файлы, которые не прошли проверку
meta_move_component(yaml_path, node_id, new_parent_id, slot, ...) перенести узел в другой (или тот же) слот; комментарии над ним едут следом
meta_move_components(yaml_path, node_ids, ...) перенести несколько узлов одной операцией, сохранив их порядок в документе
meta_remove_component(yaml_path, node_id) удалить узел вместе с его комментариями
meta_remove_components(yaml_path, node_ids) удалить несколько узлов одной операцией
meta_set_component_property(yaml_path, node_id, key, value, value_yaml) задать, заменить или удалить свойство узла
meta_add_handler(yaml_path, node_id, key, method, signature) привязать событие узла к методу-обработчику парного модуля

Каждый инструмент, который принимает узел, понимает и путь из meta_component_tree, и Имя компонента, если другого компонента с таким именем в форме нет. Путь позиционный и после правки сдвигается, имя остается. Повторное имя отклоняется со списком путей, из которых можно выбрать.

Те же операции доступны из CLI (Команды) и, для редактора, через LSP-запросы xbsl/meta*.

Супервизор сервера

xbsl-mcp запускает сервер за супервизором, который сохраняет сессию клиента, когда движок обновляют. Супервизор держит stdio, по которому говорит клиент, и запускает за собой сервер рабочим процессом: python -m xbsl.mcp_server на том же интерпретаторе, а stderr процесса уходит в журнал клиента. Супервизор запоминает initialize клиента и, когда рабочий процесс пора сменить, запускает новый и повторяет ему это рукопожатие. Клиент продолжает говорить как будто с тем же сервером, и перезапускать его не нужно.

claude mcp add xbsl -- xbsl-mcp

python -m xbsl.mcp_supervisor – та же команда, а xbsl-mcp-supervisor остается ее вторым именем. Супервизор стоит еще одного небольшого процесса и еще одной пересылки на каждое сообщение. Клиенту, которому нужен сам сервер, указывают xbsl-mcp --no-supervisor – сервер работает в том же процессе – или python -m xbsl.mcp_server; тогда обновление движка кончается отказом с просьбой перезапустить сервер, как описано выше.

self-update заменяет пакет, но не заглушки команд, а заглушка, сделанная прежним выпуском, запускает сам сервер. Такая заглушка передает свой процесс супервизору до загрузки движка, поэтому после обновления xbsl-mcp тоже запускает супервизор. Заглушку команды, которая появилась в новом выпуске, например xbsl-mcp-supervisor, создают только pip install --upgrade или pipx reinstall.

Когда процессу пора уходить, решает он сам, а супервизор читает это из его ответов:

  • отказ со stale.ran: false значит, что инструмент не выполнялся: у движка на диске другая версия, при той же версии изменились файлы его кода или изменился прочитанный процессом файл данных платформы. Супервизор отправляет тот же вызов новому процессу, и агент получает ответ нового кода и новых данных вместо отказа. version_info спрашивается заново так же: он только читает. Процесс, запущенный после одного изменения файлов, заменяется и после следующего: их различает fingerprint записи;
  • ответ, в stale которого сказано, что инструмент выполнялся (упал на смеси старого и нового кода или надстройки на диске сменились), доходит до агента как есть: инструмент мог записать файлы. Следующий вызов уходит новому процессу;
  • процесс, который упал или который остановил self-update --stop-holders, заменяется при следующем вызове, а не сразу, чтобы новый процесс не загрузил пакет, который обновление еще распаковывает. Вызов, который этот процесс выполнял, возвращается ошибкой: что он успел записать, неизвестно. Запрос, который только читает, уходит новому процессу.

Сам супервизор движок не загружает и файлов пакета открытыми не держит. self-update не считает его держателем и не останавливает, а старого кода своего он не исполняет; держатель – только рабочий процесс и сервер, запущенный с --no-supervisor. У xbsl-mcp, в котором еще работает сам сервер прежнего выпуска, рабочего процесса нет, и self-update останавливает его как держателя. Старый процесс не убивают посреди вызова: новых вызовов он не получает, заканчивает начатые и завершается. Когда работу принимает новый процесс, клиенту сообщают, что список инструментов изменился, и клиент запрашивает его снова, так что инструменты и параметры из обновления видны без перезапуска. xbsl mcp-log называет каждый замененный процесс и причину замены. Когда клиент закрывает свой конец, рабочие процессы тоже получают конец ввода, и супервизор завершается вслед за ними.

Повторное initialize просит ту версию протокола, которую просил клиент, но новый процесс может согласовать другую, чем клиенту ответили в начале сессии: обновление пакета mcp способно убрать версию, на которой говорит клиент. Супервизор тогда ничего не меняет – клиент остается при своей версии, а новый процесс работает дальше, – но записывает обе версии в журнал, и xbsl mcp-log показывает их рядом с заменой. Если клиент после обновления ведет себя странно, смотреть стоит на эту строку. Процесс, который согласовал ту же версию, такой строки не оставляет.

Веб-интерфейс

Локальная страница: укажите папку проекта и увидите замечания. Написана она на стандартной библиотеке, внешних зависимостей нет, и слушает она только 127.0.0.1.

xbsl-web            # затем открыть http://127.0.0.1:8771/

На странице есть настройки правил по тирам, выбор версии данных, фильтры по severity и по тексту, тёмная и светлая темы. Клик по замечанию открывает файл в VS Code (vscode://).

Расширение: свои правила, данные и уровни

Три группы точек расширения (entry points) позволяют отдельному пакету дополнить линтер, не отводя собственную его копию. Это нужно тем, чьи правила или языковые данные нельзя публиковать: их держат в закрытом пакете, который зависит от xbsl.

# pyproject.toml вашего пакета
dependencies = ["xbsl>=1.0.0"]

[project.entry-points."xbsl.rules"]
мой-проект = "мой_проект.rules"      # импорт модуля выполняет его декораторы @rule

[project.entry-points."xbsl.data"]
мой-проект = "мой_проект:data_root"  # путь либо функция, возвращающая путь

[project.entry-points."xbsl.severity"]
мой-проект = "мой_проект:severity_overrides"   # {id правила: "error"|"warning"|"info"|"off"}

продолжают работать: старые группы сканируются вслед за новыми.

Словарь уровней – или функция без аргументов, которая его возвращает – поднимает и понижает уровень любого правила, встроенного или из плагина, для всех запусков этой установки. Проект может считать, например, style/abbreviation-case предупреждением, пока публичное значение по умолчанию остаётся info. "off" убирает правило из набора по умолчанию; явный --select или --enable всё равно включит его, с базовым уровнем.

Пакет установлен – и CLI, и MCP-сервер, и веб-панель видят все расширения. Ключей и файлов настройки для этого не нужно. Сбой точки расширения роняет запуск, а не ограничивается предупреждением: линтер, молча потерявший правило, остаётся зелёным в CI и ничего не гарантирует. Переопределение с неизвестным id правила или уровнем падает по той же причине. XBSL_NO_PLUGINS=1 игнорирует все внешние пакеты: остаются встроенные правила, вшитые данные и уровни по умолчанию.

Последнее обновление 2 октября 2026 г.

Эта страница была полезной?