From b188d338f91b69da7d62aa028d1ad9f1bc2beba6 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Mon, 1 Jun 2026 15:40:01 +0300 Subject: [PATCH] =?UTF-8?q?feat(meta-info):=20=D0=B2=D1=8B=D0=B2=D0=BE?= =?UTF-8?q?=D0=B4=D0=B8=D1=82=D1=8C=20=C2=AB=D0=9F=D1=80=D0=B5=D0=B4=D1=81?= =?UTF-8?q?=D1=82=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D1=82=D0=B8?= =?UTF-8?q?=D0=BF=D0=B0=C2=BB=20=D0=B4=D0=BB=D1=8F=20=D1=81=D1=81=D1=8B?= =?UTF-8?q?=D0=BB=D0=BE=D1=87=D0=BD=D1=8B=D1=85=20=D0=BE=D0=B1=D1=8A=D0=B5?= =?UTF-8?q?=D0=BA=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit В шапке ссылочных объектов (справочники, документы, перечисления, ПВХ/ПВР, планы счетов, планы обмена, бизнес-процессы, задачи) теперь выводится строка «Представление типа» — имя ссылочного типа в диалогах выбора типа, с fallback ObjectPresentation -> Synonym -> Name. В режиме full дополнительно выводятся заданные сырые представления (объекта/списка и расширенные). Тесты: раннер принимает stdoutContains строкой или массивом, добавлен stdoutNotContains. Добавлены кейсы meta-info (ед.ч. ПВХ, full со всеми представлениями, fallback на синоним) и негативная проверка у регистра. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../skills/meta-info/scripts/meta-info.ps1 | 29 +++++++++++++++++- .claude/skills/meta-info/scripts/meta-info.py | 30 ++++++++++++++++++- tests/skills/README.md | 16 +++++++++- .../real-acc-catalog-presentation-full.json | 14 +++++++++ .../cases/meta-info/real-acc-register.json | 5 +++- ...eal-erp-catalog-presentation-fallback.json | 10 +++++++ .../meta-info/real-erp-cct-presentation.json | 8 +++++ tests/skills/runner.mjs | 28 +++++++++++++---- 8 files changed, 131 insertions(+), 9 deletions(-) create mode 100644 tests/skills/cases/meta-info/real-acc-catalog-presentation-full.json create mode 100644 tests/skills/cases/meta-info/real-erp-catalog-presentation-fallback.json create mode 100644 tests/skills/cases/meta-info/real-erp-cct-presentation.json diff --git a/.claude/skills/meta-info/scripts/meta-info.ps1 b/.claude/skills/meta-info/scripts/meta-info.ps1 index 7e6d658e..8c29bc2b 100644 --- a/.claude/skills/meta-info/scripts/meta-info.ps1 +++ b/.claude/skills/meta-info/scripts/meta-info.ps1 @@ -1,4 +1,4 @@ -# meta-info v1.1 — Compact summary of 1C metadata object +# meta-info v1.2 — Compact summary of 1C metadata object # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory=$true)][Alias('Path')][string]$ObjectPath, @@ -422,6 +422,22 @@ $objName = $props.SelectSingleNode("md:Name", $ns).InnerText $synNode = $props.SelectSingleNode("md:Synonym", $ns) $synonym = Get-MLText $synNode +# Presentations (type-choice dialogs show "Представление объекта" as the ref type name) +$objPresentation = Get-MLText $props.SelectSingleNode("md:ObjectPresentation", $ns) +$extObjPresentation = Get-MLText $props.SelectSingleNode("md:ExtendedObjectPresentation", $ns) +$listPresentation = Get-MLText $props.SelectSingleNode("md:ListPresentation", $ns) +$extListPresentation = Get-MLText $props.SelectSingleNode("md:ExtendedListPresentation", $ns) + +# Reference (ref-typed) metadata objects — those with a ...Ref type +$refMdTypes = @("Catalog","Document","Enum","ChartOfAccounts","ChartOfCharacteristicTypes", + "ChartOfCalculationTypes","ExchangePlan","BusinessProcess","Task") +$isRefObject = $refMdTypes -contains $mdType + +# Effective type presentation: ObjectPresentation -> Synonym -> Name +$typePresentation = if ($objPresentation) { $objPresentation } + elseif ($synonym) { $synonym } + else { $objName } + # --- Handle -Name drill-down --- $drillDone = $false if ($Name -and $childObjs) { @@ -593,6 +609,17 @@ if (-not $drillDone) { $header += " ===" Out $header + # --- Type presentation (ref objects) --- + if ($isRefObject) { + Out "Представление типа: $typePresentation" + if ($Mode -eq "full") { + if ($objPresentation) { Out "Представление объекта: $objPresentation" } + if ($extObjPresentation) { Out "Расширенное представление объекта: $extObjPresentation" } + if ($listPresentation) { Out "Представление списка: $listPresentation" } + if ($extListPresentation) { Out "Расширенное представление списка: $extListPresentation" } + } + } + # --- Mode: brief --- if ($Mode -eq "brief") { # Attributes diff --git a/.claude/skills/meta-info/scripts/meta-info.py b/.claude/skills/meta-info/scripts/meta-info.py index 61c73325..cee63692 100644 --- a/.claude/skills/meta-info/scripts/meta-info.py +++ b/.claude/skills/meta-info/scripts/meta-info.py @@ -1,4 +1,4 @@ -# meta-info v1.1 — Compact summary of 1C metadata object (Python port) +# meta-info v1.2 — Compact summary of 1C metadata object (Python port) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse import os @@ -477,6 +477,21 @@ obj_name = inner_text(find(props, "md:Name")) syn_node = find(props, "md:Synonym") synonym = get_ml_text(syn_node) +# Presentations (type-choice dialogs show "Представление объекта" as the ref type name) +obj_presentation = get_ml_text(find(props, "md:ObjectPresentation")) +ext_obj_presentation = get_ml_text(find(props, "md:ExtendedObjectPresentation")) +list_presentation = get_ml_text(find(props, "md:ListPresentation")) +ext_list_presentation = get_ml_text(find(props, "md:ExtendedListPresentation")) + +# Reference (ref-typed) metadata objects — those with a ...Ref type +ref_md_types = {"Catalog", "Document", "Enum", "ChartOfAccounts", + "ChartOfCharacteristicTypes", "ChartOfCalculationTypes", + "ExchangePlan", "BusinessProcess", "Task"} +is_ref_object = md_type in ref_md_types + +# Effective type presentation: ObjectPresentation -> Synonym -> Name +type_presentation = obj_presentation or synonym or obj_name + # ── Handle -Name drill-down ────────────────────────────────── drill_done = False @@ -636,6 +651,19 @@ if not drill_done: header += " ===" out(header) + # Type presentation (ref objects) + if is_ref_object: + out(f"Представление типа: {type_presentation}") + if mode == "full": + if obj_presentation: + out(f"Представление объекта: {obj_presentation}") + if ext_obj_presentation: + out(f"Расширенное представление объекта: {ext_obj_presentation}") + if list_presentation: + out(f"Представление списка: {list_presentation}") + if ext_list_presentation: + out(f"Расширенное представление списка: {ext_list_presentation}") + if mode == "brief": # Attributes attrs = get_attributes(child_objs) if child_objs is not None else [] diff --git a/tests/skills/README.md b/tests/skills/README.md index feb94049..f01106cf 100644 --- a/tests/skills/README.md +++ b/tests/skills/README.md @@ -108,6 +108,20 @@ node tests/skills/verify-snapshots.mjs --help # полный `params` — параметры для навыка. Используются через `case.` и `workPath` в `_skill.json`. +`expect.stdoutContains` / `expect.stdoutNotContains` — строка **или массив строк**. Каждая подстрока проверяется на наличие (`stdoutContains`) или отсутствие (`stdoutNotContains`) в stdout навыка. Удобно для info-навыков: проверить, что нужная строка есть, а лишней — нет. + +```json +{ + "name": "Представление типа у ПВХ", + "setup": "external:C:/WS/tasks/cfsrc/erp_8.3.24", + "params": { "objectPath": "ChartsOfCharacteristicTypes/ВидыСубконтоХозрасчетные" }, + "expect": { + "stdoutContains": ["Представление типа: Вид субконто", "Представление объекта: Вид субконто"], + "stdoutNotContains": "Представление списка:" + } +} +``` + ### С дополнительными CLI-аргументами ```json @@ -175,7 +189,7 @@ node tests/skills/verify-snapshots.mjs --help # полный | `outputPath` | нет | Относительный путь для навыков с `-OutputPath` | | `args_extra` | нет | Массив дополнительных CLI-аргументов | | `preRun` | нет | Массив шагов подготовки (создание объектов и т.п.) | -| `expect` | нет | Дополнительные проверки: `files`, `stdoutContains` | +| `expect` | нет | Дополнительные проверки: `files`, `stdoutContains` (строка/массив), `stdoutNotContains` (строка/массив) | | `expectError` | нет | `true` или строка — ожидается ошибка | ## Эталоны (snapshots) diff --git a/tests/skills/cases/meta-info/real-acc-catalog-presentation-full.json b/tests/skills/cases/meta-info/real-acc-catalog-presentation-full.json new file mode 100644 index 00000000..c148e345 --- /dev/null +++ b/tests/skills/cases/meta-info/real-acc-catalog-presentation-full.json @@ -0,0 +1,14 @@ +{ + "name": "Представления объекта/списка/расширенные в full — БП СтатьиДвиженияДенежныхСредств", + "setup": "external:C:/WS/tasks/cfsrc/acc_8.3.24", + "params": { "objectPath": "Catalogs/СтатьиДвиженияДенежныхСредств" }, + "args_extra": ["-Mode", "full"], + "expect": { + "stdoutContains": [ + "Представление типа: Статья движения ден. средств", + "Представление объекта: Статья движения ден. средств", + "Расширенное представление объекта: Статья движения денежных средств", + "Представление списка: Статьи движения денежных средств" + ] + } +} diff --git a/tests/skills/cases/meta-info/real-acc-register.json b/tests/skills/cases/meta-info/real-acc-register.json index 7b1006be..73cef8c0 100644 --- a/tests/skills/cases/meta-info/real-acc-register.json +++ b/tests/skills/cases/meta-info/real-acc-register.json @@ -2,5 +2,8 @@ "name": "Реальный регистр бухгалтерии Хозрасчетный (БП)", "setup": "external:C:/WS/tasks/cfsrc/acc_8.3.24", "params": { "objectPath": "AccountingRegisters/Хозрасчетный" }, - "expect": { "stdoutContains": "Хозрасчетный" } + "expect": { + "stdoutContains": "Хозрасчетный", + "stdoutNotContains": "Представление типа:" + } } diff --git a/tests/skills/cases/meta-info/real-erp-catalog-presentation-fallback.json b/tests/skills/cases/meta-info/real-erp-catalog-presentation-fallback.json new file mode 100644 index 00000000..684cd6f6 --- /dev/null +++ b/tests/skills/cases/meta-info/real-erp-catalog-presentation-fallback.json @@ -0,0 +1,10 @@ +{ + "name": "Представление типа = синоним при пустом ObjectPresentation — ERP СтатьиБюджетов", + "setup": "external:C:/WS/tasks/cfsrc/erp_8.3.24", + "params": { "objectPath": "Catalogs/СтатьиБюджетов" }, + "args_extra": ["-Mode", "full"], + "expect": { + "stdoutContains": "Представление типа: Статьи бюджетов", + "stdoutNotContains": "Представление объекта:" + } +} diff --git a/tests/skills/cases/meta-info/real-erp-cct-presentation.json b/tests/skills/cases/meta-info/real-erp-cct-presentation.json new file mode 100644 index 00000000..2441a320 --- /dev/null +++ b/tests/skills/cases/meta-info/real-erp-cct-presentation.json @@ -0,0 +1,8 @@ +{ + "name": "Представление типа (ед.ч.) у ПВХ — ERP ВидыСубконтоХозрасчетные", + "setup": "external:C:/WS/tasks/cfsrc/erp_8.3.24", + "params": { "objectPath": "ChartsOfCharacteristicTypes/ВидыСубконтоХозрасчетные" }, + "expect": { + "stdoutContains": "Представление типа: Вид субконто" + } +} diff --git a/tests/skills/runner.mjs b/tests/skills/runner.mjs index 38608973..bfaddc80 100644 --- a/tests/skills/runner.mjs +++ b/tests/skills/runner.mjs @@ -588,8 +588,17 @@ async function runCaseAsync(testCase, opts) { } } if (caseData.expect?.stdoutContains) { - if (!stdout.includes(caseData.expect.stdoutContains)) { - errors.push(`stdout does not contain "${caseData.expect.stdoutContains}"`); + const needles = Array.isArray(caseData.expect.stdoutContains) + ? caseData.expect.stdoutContains : [caseData.expect.stdoutContains]; + for (const needle of needles) { + if (!stdout.includes(needle)) errors.push(`stdout does not contain "${needle}"`); + } + } + if (caseData.expect?.stdoutNotContains) { + const needles = Array.isArray(caseData.expect.stdoutNotContains) + ? caseData.expect.stdoutNotContains : [caseData.expect.stdoutNotContains]; + for (const needle of needles) { + if (stdout.includes(needle)) errors.push(`stdout unexpectedly contains "${needle}"`); } } if (errors.length === 0 && !caseData.expectError && !workspace.readOnly) { @@ -754,10 +763,19 @@ function runCase(testCase, opts) { } } - // expect.stdoutContains + // expect.stdoutContains / stdoutNotContains (string or array) if (caseData.expect?.stdoutContains) { - if (!stdout.includes(caseData.expect.stdoutContains)) { - errors.push(`stdout does not contain "${caseData.expect.stdoutContains}"`); + const needles = Array.isArray(caseData.expect.stdoutContains) + ? caseData.expect.stdoutContains : [caseData.expect.stdoutContains]; + for (const needle of needles) { + if (!stdout.includes(needle)) errors.push(`stdout does not contain "${needle}"`); + } + } + if (caseData.expect?.stdoutNotContains) { + const needles = Array.isArray(caseData.expect.stdoutNotContains) + ? caseData.expect.stdoutNotContains : [caseData.expect.stdoutNotContains]; + for (const needle of needles) { + if (stdout.includes(needle)) errors.push(`stdout unexpectedly contains "${needle}"`); } }