From 422e3973811db0cc7015c87cb0cd0e1764e9dda0 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Mon, 9 Mar 2026 18:14:44 +0300 Subject: [PATCH] feat(validate): brief output by default, -Detailed for verbose All 10 validation skills (meta, epf, skd, cf, cfe, form, mxl, role, subsystem, interface) now output a single summary line on success: === Validation OK: Type.Name (N checks) === Errors/warnings always shown. Full per-check [OK] output behind -Detailed flag. Removed all N/A check lines. Unified role-validate output format. Trimmed SKILL.md files from 42-119 to 51-67 lines. Version bumps: meta-validate v1.2, all others v1.1. Co-Authored-By: Claude Opus 4.6 --- .claude/skills/cf-validate/SKILL.md | 60 +++---- .../cf-validate/scripts/cf-validate.ps1 | 22 ++- .../skills/cf-validate/scripts/cf-validate.py | 29 +++- .claude/skills/cfe-validate/SKILL.md | 34 ++-- .../cfe-validate/scripts/cfe-validate.ps1 | 22 ++- .../cfe-validate/scripts/cfe-validate.py | 31 ++-- .claude/skills/epf-validate/SKILL.md | 60 ++----- .../epf-validate/scripts/epf-validate.ps1 | 24 +-- .../epf-validate/scripts/epf-validate.py | 36 ++-- .claude/skills/form-validate/SKILL.md | 60 ++----- .../form-validate/scripts/form-validate.ps1 | 38 ++-- .../form-validate/scripts/form-validate.py | 47 +++-- .claude/skills/interface-validate/SKILL.md | 59 ++----- .../scripts/interface-validate.ps1 | 23 ++- .../scripts/interface-validate.py | 47 +++-- .claude/skills/meta-validate/SKILL.md | 85 ++------- .../meta-validate/scripts/meta-validate.ps1 | 42 ++--- .../meta-validate/scripts/meta-validate.py | 42 ++--- .claude/skills/mxl-validate/SKILL.md | 55 ++---- .../mxl-validate/scripts/mxl-validate.ps1 | 33 ++-- .../mxl-validate/scripts/mxl-validate.py | 48 +++--- .claude/skills/role-validate/SKILL.md | 108 +++--------- .../role-validate/scripts/role-validate.ps1 | 163 ++++++++++-------- .../role-validate/scripts/role-validate.py | 126 ++++++++------ .claude/skills/skd-validate/SKILL.md | 58 +++---- .../skd-validate/scripts/skd-validate.ps1 | 20 ++- .../skd-validate/scripts/skd-validate.py | 20 ++- .claude/skills/subsystem-validate/SKILL.md | 54 +++--- .../scripts/subsystem-validate.ps1 | 25 ++- .../scripts/subsystem-validate.py | 30 ++-- 30 files changed, 685 insertions(+), 816 deletions(-) diff --git a/.claude/skills/cf-validate/SKILL.md b/.claude/skills/cf-validate/SKILL.md index ab4c25fd..8d67bd09 100644 --- a/.claude/skills/cf-validate/SKILL.md +++ b/.claude/skills/cf-validate/SKILL.md @@ -1,7 +1,7 @@ --- name: cf-validate description: Валидация конфигурации 1С. Используй после создания или модификации конфигурации для проверки корректности -argument-hint: [-MaxErrors 30] +argument-hint: [-Detailed] [-MaxErrors 30] allowed-tools: - Bash - Read @@ -12,19 +12,31 @@ allowed-tools: Проверяет Configuration.xml на структурные ошибки: XML well-formedness, InternalInfo, свойства, enum-значения, ChildObjects, DefaultLanguage, файлы языков, каталоги объектов. -## Параметры и команда +## Использование -| Параметр | Описание | -|----------|----------| -| `ConfigPath` | Путь к Configuration.xml или каталогу выгрузки | -| `MaxErrors` | Остановиться после N ошибок (default: 30) | -| `OutFile` | Записать результат в файл (UTF-8 BOM) | +``` +/cf-validate +/cf-validate upload/cfempty — каталог выгрузки +``` + +`ConfigPath` авторезолв: если указана директория — ищет `Configuration.xml`. + +## Параметры + +| Параметр | Обяз. | Умолч. | Описание | +|------------|:-----:|---------|-------------------------------------------------| +| ConfigPath | да | — | Путь к Configuration.xml или каталогу выгрузки | +| Detailed | нет | — | Показывать [OK] для каждой проверки | +| MaxErrors | нет | 30 | Остановиться после N ошибок | +| OutFile | нет | — | Записать результат в файл (UTF-8 BOM) | + +## Команда ```powershell powershell.exe -NoProfile -File .claude/skills/cf-validate/scripts/cf-validate.ps1 -ConfigPath "<путь>" ``` -## Выполняемые проверки +## Проверки | # | Проверка | Серьёзность | |---|----------|-------------| @@ -37,34 +49,4 @@ powershell.exe -NoProfile -File .claude/skills/cf-validate/scripts/cf-validate.p | 7 | Файлы языков Languages/.xml существуют | WARN | | 8 | Каталоги объектов из ChildObjects существуют (spot-check) | WARN | -## Вывод - -``` -=== Validation: Configuration.МояКонфигурация === - -[OK] 1. Root structure: MetaDataObject/Configuration, version 2.17 -[OK] 2. InternalInfo: 7 ContainedObject, all ClassIds valid -[OK] 3. Properties: Name="МояКонфигурация", Synonym present -[OK] 4. Property values: 11 enum properties checked -[OK] 5. ChildObjects: 1 types, 1 objects, order correct -[OK] 6. DefaultLanguage "Language.Русский" found in ChildObjects -[OK] 7. Language files: 1/1 exist -[OK] 8. Object directories: spot-check passed - -=== Result: 0 errors, 0 warnings === -``` - -Exit code: 0 = OK, 1 = errors. - -## Примеры - -```powershell -# Пустая конфигурация -... -ConfigPath upload/cfempty - -# Реальная конфигурация -... -ConfigPath C:\WS\tasks\cfsrc\acc_8.3.24 - -# С лимитом ошибок -... -ConfigPath test-tmp/cf -MaxErrors 10 -``` +Exit code: 0 = OK, 1 = есть ошибки. По умолчанию краткий вывод. `-Detailed` для поштучной детализации. diff --git a/.claude/skills/cf-validate/scripts/cf-validate.ps1 b/.claude/skills/cf-validate/scripts/cf-validate.ps1 index c0c6fed3..ad13c404 100644 --- a/.claude/skills/cf-validate/scripts/cf-validate.ps1 +++ b/.claude/skills/cf-validate/scripts/cf-validate.ps1 @@ -1,9 +1,11 @@ -# cf-validate v1.0 — Validate 1C configuration root structure +# cf-validate v1.1 — Validate 1C configuration root structure # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)] [string]$ConfigPath, + [switch]$Detailed, + [int]$MaxErrors = 30, [string]$OutFile @@ -38,6 +40,7 @@ $configDir = Split-Path $resolvedPath -Parent # --- Output infrastructure --- $script:errors = 0 $script:warnings = 0 +$script:okCount = 0 $script:stopped = $false $script:output = New-Object System.Text.StringBuilder 8192 @@ -48,7 +51,8 @@ function Out-Line { function Report-OK { param([string]$msg) - Out-Line "[OK] $msg" + $script:okCount++ + if ($Detailed) { Out-Line "[OK] $msg" } } function Report-Error { @@ -67,10 +71,14 @@ function Report-Warn { } $finalize = { - Out-Line "" - Out-Line "=== Result: $($script:errors) errors, $($script:warnings) warnings ===" - - $result = $script:output.ToString() + $checks = $script:okCount + $script:errors + $script:warnings + if ($script:errors -eq 0 -and $script:warnings -eq 0 -and -not $Detailed) { + $result = "=== Validation OK: Configuration.$objName ($checks checks) ===" + } else { + Out-Line "" + Out-Line "=== Result: $($script:errors) errors, $($script:warnings) warnings ($checks checks) ===" + $result = $script:output.ToString() + } Write-Host $result if ($OutFile) { @@ -525,8 +533,6 @@ if ($childObjNode) { Report-Warn "8. Missing directory: $md" } } -} else { - Report-OK "8. Object directories: N/A" } # --- Final output --- diff --git a/.claude/skills/cf-validate/scripts/cf-validate.py b/.claude/skills/cf-validate/scripts/cf-validate.py index 43e1cc3d..0c04ce77 100644 --- a/.claude/skills/cf-validate/scripts/cf-validate.py +++ b/.claude/skills/cf-validate/scripts/cf-validate.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# cf-validate v1.0 — Validate 1C configuration XML structure +# cf-validate v1.1 — Validate 1C configuration XML structure # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills """Validates Configuration.xml: root structure, InternalInfo, properties, ChildObjects, languages.""" import sys, os, argparse, re @@ -108,18 +108,23 @@ EXPECTED_NS = 'http://v8.1c.ru/8.3/MDClasses' class Reporter: - def __init__(self, max_errors): + def __init__(self, max_errors, detailed=False): self.errors = 0 self.warnings = 0 + self.ok_count = 0 self.stopped = False self.max_errors = max_errors + self.detailed = detailed self.lines = [] + self.obj_name = '(unknown)' def out(self, msg=''): self.lines.append(msg) def ok(self, msg): - self.lines.append(f'[OK] {msg}') + self.ok_count += 1 + if self.detailed: + self.lines.append(f'[OK] {msg}') def error(self, msg): self.errors += 1 @@ -135,11 +140,15 @@ class Reporter: return '\r\n'.join(self.lines) + '\r\n' def finalize(self, out_file): - self.out('') - self.out(f'=== Result: {self.errors} errors, {self.warnings} warnings ===') + checks = self.ok_count + self.errors + self.warnings + if self.errors == 0 and self.warnings == 0 and not self.detailed: + result = f'=== Validation OK: Configuration.{self.obj_name} ({checks} checks) ===' + else: + self.out('') + self.out(f'=== Result: {self.errors} errors, {self.warnings} warnings ({checks} checks) ===') + result = self.text() - result = self.text() - print(result, end='') + print(result, end='' if '\r\n' in result else '\n') if out_file: with open(out_file, 'w', encoding='utf-8-sig', newline='') as f: @@ -154,6 +163,7 @@ def main(): description='Validate 1C configuration XML structure', allow_abbrev=False ) parser.add_argument('-ConfigPath', dest='ConfigPath', required=True) + parser.add_argument('-Detailed', action='store_true') parser.add_argument('-MaxErrors', dest='MaxErrors', type=int, default=30) parser.add_argument('-OutFile', dest='OutFile', default='') args = parser.parse_args() @@ -184,7 +194,7 @@ def main(): if out_file and not os.path.isabs(out_file): out_file = os.path.join(os.getcwd(), out_file) - r = Reporter(max_errors) + r = Reporter(max_errors, detailed=args.Detailed) r.out('') # --- 1. Parse XML --- @@ -248,6 +258,7 @@ def main(): props_node = cfg_node.find('md:Properties', NS) name_node = props_node.find('md:Name', NS) if props_node is not None else None obj_name = (name_node.text or '') if name_node is not None and name_node.text else '(unknown)' + r.obj_name = obj_name r.lines.insert(0, f'=== Validation: Configuration.{obj_name} ===') @@ -521,7 +532,7 @@ def main(): for md in missing_dirs: r.warn(f'8. Missing directory: {md}') else: - r.ok('8. Object directories: N/A') + pass # no ChildObjects # --- Final output --- r.finalize(out_file) diff --git a/.claude/skills/cfe-validate/SKILL.md b/.claude/skills/cfe-validate/SKILL.md index 131dd798..4bb2856b 100644 --- a/.claude/skills/cfe-validate/SKILL.md +++ b/.claude/skills/cfe-validate/SKILL.md @@ -1,24 +1,34 @@ --- name: cfe-validate description: Валидация расширения конфигурации 1С (CFE). Используй после создания или модификации расширения для проверки корректности -argument-hint: [-MaxErrors 30] +argument-hint: [-Detailed] [-MaxErrors 30] allowed-tools: - Bash - Read - Glob --- -# /cfe-validate — Валидация расширения конфигурации +# /cfe-validate — валидация расширения конфигурации (CFE) Проверяет структурную корректность расширения: XML-формат, свойства, состав, заимствованные объекты. Аналог `/cf-validate`, но для расширений. +## Использование + +``` +/cfe-validate +/cfe-validate src — каталог расширения +``` + +`ExtensionPath` авторезолв: если указана директория — ищет `Configuration.xml`. + ## Параметры -| Параметр | Описание | По умолчанию | -|----------|----------|--------------| -| `ExtensionPath` | Путь к каталогу или Configuration.xml расширения (обязат.) | — | -| `MaxErrors` | Лимит ошибок | 30 | -| `OutFile` | Записать результат в файл | — | +| Параметр | Обяз. | Умолч. | Описание | +|---------------|:-----:|---------|-------------------------------------------------| +| ExtensionPath | да | — | Путь к каталогу или Configuration.xml расширения | +| Detailed | нет | — | Показывать [OK] для каждой проверки | +| MaxErrors | нет | 30 | Остановиться после N ошибок | +| OutFile | нет | — | Записать результат в файл | ## Команда @@ -40,12 +50,4 @@ powershell.exe -NoProfile -File .claude/skills/cfe-validate/scripts/cfe-validate | 8 | Каталоги объектов существуют | WARN | | 9 | Заимствованные объекты: ObjectBelonging=Adopted, ExtendedConfigurationObject UUID | ERROR/WARN | -## Пример вывода - -``` -=== Validation: Extension.МоёРасширение === -[OK] 1. Root structure: MetaDataObject/Configuration, version 2.17 -[OK] 2. InternalInfo: 7 ContainedObject, all ClassIds valid -... -=== Result: 0 errors, 0 warnings === -``` +Exit code: 0 = OK, 1 = есть ошибки. По умолчанию краткий вывод. `-Detailed` для поштучной детализации. diff --git a/.claude/skills/cfe-validate/scripts/cfe-validate.ps1 b/.claude/skills/cfe-validate/scripts/cfe-validate.ps1 index ab03fdd9..76c44101 100644 --- a/.claude/skills/cfe-validate/scripts/cfe-validate.ps1 +++ b/.claude/skills/cfe-validate/scripts/cfe-validate.ps1 @@ -1,9 +1,11 @@ -# cfe-validate v1.0 — Validate 1C configuration extension structure (CFE) +# cfe-validate v1.1 — Validate 1C configuration extension structure (CFE) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)] [string]$ExtensionPath, + [switch]$Detailed, + [int]$MaxErrors = 30, [string]$OutFile @@ -38,6 +40,7 @@ $configDir = Split-Path $resolvedPath -Parent # --- Output infrastructure --- $script:errors = 0 $script:warnings = 0 +$script:okCount = 0 $script:stopped = $false $script:output = New-Object System.Text.StringBuilder 8192 @@ -48,7 +51,8 @@ function Out-Line { function Report-OK { param([string]$msg) - Out-Line "[OK] $msg" + $script:okCount++ + if ($Detailed) { Out-Line "[OK] $msg" } } function Report-Error { @@ -67,10 +71,14 @@ function Report-Warn { } $finalize = { - Out-Line "" - Out-Line "=== Result: $($script:errors) errors, $($script:warnings) warnings ===" - - $result = $script:output.ToString() + $checks = $script:okCount + $script:errors + $script:warnings + if ($script:errors -eq 0 -and $script:warnings -eq 0 -and -not $Detailed) { + $result = "=== Validation OK: Extension.$objName ($checks checks) ===" + } else { + Out-Line "" + Out-Line "=== Result: $($script:errors) errors, $($script:warnings) warnings ($checks checks) ===" + $result = $script:output.ToString() + } Write-Host $result if ($OutFile) { @@ -521,8 +529,6 @@ if ($childObjNode) { Report-Warn "8. Missing directory: $md" } } -} else { - Report-OK "8. Object directories: N/A" } if ($script:stopped) { & $finalize; exit 1 } diff --git a/.claude/skills/cfe-validate/scripts/cfe-validate.py b/.claude/skills/cfe-validate/scripts/cfe-validate.py index 2a0dd5f0..40d24db0 100644 --- a/.claude/skills/cfe-validate/scripts/cfe-validate.py +++ b/.claude/skills/cfe-validate/scripts/cfe-validate.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# cfe-validate v1.0 — Validate 1C configuration extension XML structure (CFE) +# cfe-validate v1.1 — Validate 1C configuration extension XML structure (CFE) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills """Validates extension Configuration.xml: root, InternalInfo, extension properties, ChildObjects, borrowed objects.""" import sys, os, argparse, re @@ -93,18 +93,23 @@ EXPECTED_NS = 'http://v8.1c.ru/8.3/MDClasses' class Reporter: - def __init__(self, max_errors): + def __init__(self, max_errors, detailed=False): self.errors = 0 self.warnings = 0 + self.ok_count = 0 self.stopped = False self.max_errors = max_errors + self.detailed = detailed self.lines = [] + self.obj_name = '(unknown)' def out(self, msg=''): self.lines.append(msg) def ok(self, msg): - self.lines.append(f'[OK] {msg}') + self.ok_count += 1 + if self.detailed: + self.lines.append(f'[OK] {msg}') def error(self, msg): self.errors += 1 @@ -120,11 +125,15 @@ class Reporter: return '\r\n'.join(self.lines) + '\r\n' def finalize(self, out_file): - self.out('') - self.out(f'=== Result: {self.errors} errors, {self.warnings} warnings ===') + checks = self.ok_count + self.errors + self.warnings + if self.errors == 0 and self.warnings == 0 and not self.detailed: + result = f'=== Validation OK: Extension.{self.obj_name} ({checks} checks) ===' + else: + self.out('') + self.out(f'=== Result: {self.errors} errors, {self.warnings} warnings ({checks} checks) ===') + result = self.text() - result = self.text() - print(result, end='') + print(result, end='' if '\r\n' in result else '\n') if out_file: with open(out_file, 'w', encoding='utf-8-sig', newline='') as f: @@ -139,6 +148,7 @@ def main(): description='Validate 1C configuration extension XML structure (CFE)', allow_abbrev=False ) parser.add_argument('-ExtensionPath', dest='ExtensionPath', required=True) + parser.add_argument('-Detailed', action='store_true') parser.add_argument('-MaxErrors', dest='MaxErrors', type=int, default=30) parser.add_argument('-OutFile', dest='OutFile', default='') args = parser.parse_args() @@ -169,7 +179,7 @@ def main(): if out_file and not os.path.isabs(out_file): out_file = os.path.join(os.getcwd(), out_file) - r = Reporter(max_errors) + r = Reporter(max_errors, detailed=args.Detailed) r.out('') # --- 1. Parse XML --- @@ -233,6 +243,7 @@ def main(): props_node = cfg_node.find('md:Properties', NS) name_node = props_node.find('md:Name', NS) if props_node is not None else None obj_name = (name_node.text or '') if name_node is not None and name_node.text else '(unknown)' + r.obj_name = obj_name r.lines.insert(0, f'=== Validation: Extension.{obj_name} ===') @@ -512,7 +523,7 @@ def main(): for md in missing_dirs: r.warn(f'8. Missing directory: {md}') else: - r.ok('8. Object directories: N/A') + pass # no ChildObjects if r.stopped: r.finalize(out_file) @@ -583,7 +594,7 @@ def main(): break if borrowed_count == 0: - r.ok('9. Borrowed objects: none found') + pass # no borrowed objects elif check9_ok: r.ok(f'9. Borrowed objects: {borrowed_ok_count}/{borrowed_count} validated') diff --git a/.claude/skills/epf-validate/SKILL.md b/.claude/skills/epf-validate/SKILL.md index c253b434..116e15d8 100644 --- a/.claude/skills/epf-validate/SKILL.md +++ b/.claude/skills/epf-validate/SKILL.md @@ -1,7 +1,7 @@ --- name: epf-validate description: Валидация внешней обработки 1С (EPF). Используй после создания или модификации обработки для проверки корректности -argument-hint: [-MaxErrors 30] +argument-hint: [-Detailed] [-MaxErrors 30] allowed-tools: - Bash - Read @@ -10,25 +10,25 @@ allowed-tools: # /epf-validate — валидация внешней обработки (EPF) -Проверяет структурную корректность XML-исходников внешней обработки: корневую структуру, InternalInfo, свойства, ChildObjects, реквизиты, табличные части, уникальность имён, наличие файлов форм и макетов. - -Скрипт также работает для внешних отчётов (ERF) — автоопределение по типу элемента. См. `/erf-validate`. +Проверяет структурную корректность XML-исходников внешней обработки: корневую структуру, InternalInfo, свойства, ChildObjects, реквизиты, табличные части, уникальность имён, наличие файлов форм и макетов. Также работает для внешних отчётов (ERF). ## Использование ``` /epf-validate +/epf-validate src/МояОбработка — авторезолв в /.xml ``` +`ObjectPath` авторезолв: если указана директория — ищет `/.xml`. + ## Параметры -| Параметр | Обязательный | По умолчанию | Описание | -|------------|:------------:|--------------|-------------------------------------------------| -| ObjectPath | да | — | Путь к корневому XML или каталогу обработки | -| MaxErrors | нет | 30 | Остановиться после N ошибок | -| OutFile | нет | — | Записать результат в файл (UTF-8 BOM) | - -`ObjectPath` авторезолв: если указана директория — ищет `/.xml`. +| Параметр | Обяз. | Умолч. | Описание | +|------------|:-----:|---------|-------------------------------------------------| +| ObjectPath | да | — | Путь к корневому XML или каталогу обработки | +| Detailed | нет | — | Показывать [OK] для каждой проверки | +| MaxErrors | нет | 30 | Остановиться после N ошибок | +| OutFile | нет | — | Записать результат в файл (UTF-8 BOM) | ## Команда @@ -36,7 +36,7 @@ allowed-tools: powershell.exe -NoProfile -File .claude/skills/epf-validate/scripts/epf-validate.ps1 -ObjectPath "<путь>" ``` -## Выполняемые проверки +## Проверки | # | Проверка | Серьёзность | |----|-------------------------------------------------------|--------------| @@ -51,38 +51,4 @@ powershell.exe -NoProfile -File .claude/skills/epf-validate/scripts/epf-validate | 9 | Файлы: формы (.xml + Ext/Form.xml), макеты | ERROR | | 10 | Дескрипторы форм: корневая структура, uuid, Name, FormType | ERROR / WARN | -## Вывод - -``` -=== Validation: EPF.МояОбработка === - -[OK] 1. Root structure: MetaDataObject/ExternalDataProcessor, version 2.17 -[OK] 2. InternalInfo: ClassId correct, 1 GeneratedType -[OK] 3. Properties: Name="МояОбработка", Synonym present, DefaultForm set -[OK] 4. ChildObjects: Attribute(3), TabularSection(1), Form(1) -[OK] 5. Cross-references: DefaultForm valid -[OK] 6. Attributes: 3 checked (UUID, Name, Type) -[OK] 7. TabularSections: 1 sections, 5 inner attributes -[OK] 8. Name uniqueness: 6 names, all unique -[OK] 9. File existence: 3 files verified -[OK] 10. Form descriptors: 1 checked - -=== Result: 0 errors, 0 warnings === -``` - -Код возврата: 0 = все проверки пройдены, 1 = есть ошибки. - -## Верификация - -``` -/epf-init — создать обработку -/epf-validate src/.xml — проверить результат -/epf-build — собрать EPF -``` - -## Когда использовать - -- **После `/epf-init`**: проверить scaffold -- **После добавления формы/макета**: убедиться что ChildObjects, файлы и ссылки корректны -- **После ручного редактирования XML**: выявить структурные ошибки до сборки -- **При отладке сборки**: найти причину ошибки Designer +Exit code: 0 = OK, 1 = есть ошибки. По умолчанию краткий вывод. `-Detailed` для поштучной детализации. diff --git a/.claude/skills/epf-validate/scripts/epf-validate.ps1 b/.claude/skills/epf-validate/scripts/epf-validate.ps1 index 58c090c0..91872cef 100644 --- a/.claude/skills/epf-validate/scripts/epf-validate.ps1 +++ b/.claude/skills/epf-validate/scripts/epf-validate.ps1 @@ -1,10 +1,12 @@ -# epf-validate v1.0 — Validate 1C external data processor / report structure +# epf-validate v1.1 — Validate 1C external data processor / report structure # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Works for both EPF (ExternalDataProcessor) and ERF (ExternalReport) — auto-detects param( [Parameter(Mandatory)] [string]$ObjectPath, + [switch]$Detailed, + [int]$MaxErrors = 30, [string]$OutFile @@ -60,6 +62,7 @@ $srcDir = Split-Path $resolvedPath -Parent $script:errors = 0 $script:warnings = 0 +$script:okCount = 0 $script:stopped = $false $script:output = New-Object System.Text.StringBuilder 8192 @@ -70,7 +73,8 @@ function Out-Line { function Report-OK { param([string]$msg) - Out-Line "[OK] $msg" + $script:okCount++ + if ($Detailed) { Out-Line "[OK] $msg" } } function Report-Error { @@ -89,10 +93,14 @@ function Report-Warn { } $finalize = { - Out-Line "" - Out-Line "=== Result: $($script:errors) errors, $($script:warnings) warnings ===" - - $result = $script:output.ToString() + $checks = $script:okCount + $script:errors + $script:warnings + if ($script:errors -eq 0 -and $script:warnings -eq 0 -and -not $Detailed) { + $result = "=== Validation OK: $shortType.$objName ($checks checks) ===" + } else { + Out-Line "" + Out-Line "=== Result: $($script:errors) errors, $($script:warnings) warnings ($checks checks) ===" + $result = $script:output.ToString() + } Write-Host $result if ($OutFile) { @@ -554,8 +562,6 @@ if ($childObjNode) { } else { Report-OK "6. Attributes: none" } -} else { - Report-OK "6. Attributes: N/A" } if ($script:stopped) { & $finalize; exit 1 } @@ -631,8 +637,6 @@ if ($childObjNode) { } else { Report-OK "7. TabularSections: none" } -} else { - Report-OK "7. TabularSections: N/A" } if ($script:stopped) { & $finalize; exit 1 } diff --git a/.claude/skills/epf-validate/scripts/epf-validate.py b/.claude/skills/epf-validate/scripts/epf-validate.py index b0e3a7e2..7bcc5f9d 100644 --- a/.claude/skills/epf-validate/scripts/epf-validate.py +++ b/.claude/skills/epf-validate/scripts/epf-validate.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# epf-validate v1.0 — Validate 1C external data processor / report structure +# epf-validate v1.1 — Validate 1C external data processor / report structure # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Works for both EPF (ExternalDataProcessor) and ERF (ExternalReport) — auto-detects @@ -47,6 +47,7 @@ def main(): sys.stderr.reconfigure(encoding="utf-8") parser = argparse.ArgumentParser(description="Validate 1C external data processor/report structure", allow_abbrev=False) parser.add_argument("-ObjectPath", required=True) + parser.add_argument("-Detailed", action="store_true") parser.add_argument("-MaxErrors", type=int, default=30) parser.add_argument("-OutFile", default=None) args = parser.parse_args() @@ -91,8 +92,10 @@ def main(): src_dir = os.path.dirname(resolved_path) # --- Output infrastructure --- + detailed = args.Detailed errors = 0 warnings = 0 + ok_count = 0 stopped = False output_lines = [] @@ -100,7 +103,10 @@ def main(): output_lines.append(msg) def report_ok(msg): - out_line(f"[OK] {msg}") + nonlocal ok_count + ok_count += 1 + if detailed: + out_line(f"[OK] {msg}") def report_error(msg): nonlocal errors, stopped @@ -115,9 +121,13 @@ def main(): out_line(f"[WARN] {msg}") def finalize(): - out_line("") - out_line(f"=== Result: {errors} errors, {warnings} warnings ===") - result = "\n".join(output_lines) + checks = ok_count + errors + warnings + if errors == 0 and warnings == 0 and not detailed: + result = f"=== Validation OK: {short_type}.{obj_name} ({checks} checks) ===" + else: + out_line("") + out_line(f"=== Result: {errors} errors, {warnings} warnings ({checks} checks) ===") + result = "\n".join(output_lines) print(result) if args.OutFile: with open(args.OutFile, "w", encoding="utf-8-sig") as fh: @@ -352,7 +362,7 @@ def main(): else: report_ok("4. ChildObjects: empty") else: - report_ok("4. ChildObjects: absent") + pass # no ChildObjects — nothing to check if stopped: finalize() @@ -400,7 +410,7 @@ def main(): if refs: report_ok(f"5. Cross-references: {', '.join(refs)} valid") else: - report_ok("5. Cross-references: none to check") + pass # no cross-references to check if stopped: finalize() @@ -461,9 +471,9 @@ def main(): if check6_ok: report_ok(f"6. Attributes: {attr_count} checked (UUID, Name, Type)") else: - report_ok("6. Attributes: none") + pass # no attributes else: - report_ok("6. Attributes: N/A") + pass # no ChildObjects if stopped: finalize() @@ -527,9 +537,9 @@ def main(): if check7_ok: report_ok(f"7. TabularSections: {ts_count} sections, {ts_attr_total} inner attributes") else: - report_ok("7. TabularSections: none") + pass # no tabular sections else: - report_ok("7. TabularSections: N/A") + pass # no ChildObjects if stopped: finalize() @@ -628,7 +638,7 @@ def main(): if files_checked > 0: report_ok(f"9. File existence: {files_checked} files verified") else: - report_ok("9. File existence: no forms/templates to check") + pass # no forms/templates to check if stopped: finalize() @@ -684,7 +694,7 @@ def main(): if forms_checked > 0: report_ok(f"10. Form descriptors: {forms_checked} checked") else: - report_ok("10. Form descriptors: none to check") + pass # no form descriptors to check # --- Final output --- finalize() diff --git a/.claude/skills/form-validate/SKILL.md b/.claude/skills/form-validate/SKILL.md index b781eba8..9a07c493 100644 --- a/.claude/skills/form-validate/SKILL.md +++ b/.claude/skills/form-validate/SKILL.md @@ -1,29 +1,31 @@ --- name: form-validate -description: Валидация управляемой формы 1С. Используй после создания или модификации формы для проверки корректности -argument-hint: +description: Валидация управляемой формы 1С. Используй после создания или модификации формы для проверки корректности. При наличии BaseForm автоматически проверяет callType и ID расширений +argument-hint: [-Detailed] [-MaxErrors 30] allowed-tools: - Bash - Read - Glob --- -# /form-validate — Валидатор формы +# /form-validate — валидация управляемой формы 1С -Проверяет Form.xml управляемой формы на структурные ошибки: уникальность ID, наличие companion-элементов, корректность ссылок DataPath и команд. +Проверяет Form.xml на структурные ошибки: уникальность ID, наличие companion-элементов, корректность ссылок DataPath и команд. ## Использование ``` /form-validate +/form-validate src/МояОбработка/Forms/Форма/Ext/Form.xml ``` ## Параметры -| Параметр | Обязательный | По умолчанию | Описание | -|-----------|:------------:|--------------|-----------------------------| -| FormPath | да | — | Путь к файлу Form.xml | -| MaxErrors | нет | 30 | Остановиться после N ошибок | +| Параметр | Обяз. | Умолч. | Описание | +|-----------|:-----:|---------|-----------------------------------------| +| FormPath | да | — | Путь к файлу Form.xml | +| Detailed | нет | — | Показывать [OK] для каждой проверки | +| MaxErrors | нет | 30 | Остановиться после N ошибок | ## Команда @@ -31,7 +33,7 @@ allowed-tools: powershell.exe -NoProfile -File .claude/skills/form-validate/scripts/form-validate.ps1 -FormPath "<путь>" ``` -## Выполняемые проверки +## Проверки | # | Проверка | Серьёзность | |---|---|---| @@ -51,42 +53,4 @@ powershell.exe -NoProfile -File .claude/skills/form-validate/scripts/form-valida | 14 | ID расширения >= 1000000 для добавленных attrs/commands | WARN | | 15 | callType без BaseForm — некорректная структура | WARN | -## Вывод - -``` -=== Validation: ФормаДокумента === - -[OK] Root element: Form version=2.17 -[OK] AutoCommandBar: name='ФормаКоманднаяПанель', id=-1 -[OK] Unique element IDs: 96 elements -[OK] Unique attribute IDs: 38 entries -[OK] Unique command IDs: 5 entries -[OK] Companion elements: 86 elements checked -[OK] DataPath references: 53 paths checked -[OK] Command references: 2 buttons checked -[OK] Event handlers: 41 events checked -[OK] Command actions: 5 commands checked -[OK] MainAttribute: 1 main attribute - ---- -Total: 96 elements, 38 attributes, 5 commands -All checks passed. -``` - -Код возврата: 0 = все проверки пройдены, 1 = есть ошибки. - -### Расширения - -При обнаружении `` автоматически активируются дополнительные проверки: -- Валидность значений `callType` (Before/After/Override) -- ID расширения >= 1000000 для добавленных атрибутов и команд -- Наличие version на `` - -Формы без `` проверяются только стандартными проверками. - -## Когда использовать - -- **После `/form-compile`**: проверить корректность сгенерированной формы -- **После `/form-edit`**: проверить добавленные элементы, особенно в extension-формах -- **После ручного редактирования Form.xml**: убедиться что ID уникальны, companions на месте, ссылки валидны -- **При отладке**: выявить ошибки в структуре формы до сборки EPF +Exit code: 0 = OK, 1 = есть ошибки. По умолчанию краткий вывод. `-Detailed` для поштучной детализации. diff --git a/.claude/skills/form-validate/scripts/form-validate.ps1 b/.claude/skills/form-validate/scripts/form-validate.ps1 index fbdbdc32..900ee59a 100644 --- a/.claude/skills/form-validate/scripts/form-validate.ps1 +++ b/.claude/skills/form-validate/scripts/form-validate.ps1 @@ -1,9 +1,11 @@ -# form-validate v1.0 — Validate 1C managed form +# form-validate v1.1 — Validate 1C managed form # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)] [string]$FormPath, + [switch]$Detailed, + [int]$MaxErrors = 30 ) @@ -40,10 +42,12 @@ $root = $xmlDoc.DocumentElement $errors = 0 $warnings = 0 $stopped = $false +$script:okCount = 0 function Report-OK { param([string]$msg) - Write-Host "[OK] $msg" + $script:okCount++ + if ($Detailed) { Write-Host "[OK] $msg" } } function Report-Error { @@ -73,8 +77,10 @@ if ($parentDir) { } } -Write-Host "=== Validation: $formName ===" -Write-Host "" +if ($Detailed) { + Write-Host "=== Validation: $formName ===" + Write-Host "" +} # Early BaseForm detection (used in Check 5 to skip base element DataPath validation) $hasBaseForm = ($root.SelectSingleNode("f:BaseForm", $nsMgr) -ne $null) @@ -642,18 +648,22 @@ if (-not $stopped -and -not $isExtension) { # --- Summary --- -Write-Host "" -Write-Host "---" -Write-Host "Total: $($allElements.Count) elements, $($attrNodes.Count) attributes, $($cmdNodes.Count) commands" +$checks = $script:okCount + $errors + $warnings -if ($stopped) { - Write-Host "Stopped after $MaxErrors errors. Fix and re-run." -} - -if ($errors -eq 0 -and $warnings -eq 0) { - Write-Host "All checks passed." +if ($errors -eq 0 -and $warnings -eq 0 -and -not $Detailed) { + Write-Host "=== Validation OK: Form.$formName ($checks checks) ===" } else { - Write-Host "Errors: $errors, Warnings: $warnings" + Write-Host "" + if ($Detailed) { + Write-Host "---" + Write-Host "Total: $($allElements.Count) elements, $($attrNodes.Count) attributes, $($cmdNodes.Count) commands" + } + + if ($stopped) { + Write-Host "Stopped after $MaxErrors errors. Fix and re-run." + } + + Write-Host "=== Result: $errors errors, $warnings warnings ($checks checks) ===" } if ($errors -gt 0) { diff --git a/.claude/skills/form-validate/scripts/form-validate.py b/.claude/skills/form-validate/scripts/form-validate.py index c78d0c00..13042016 100644 --- a/.claude/skills/form-validate/scripts/form-validate.py +++ b/.claude/skills/form-validate/scripts/form-validate.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# form-validate v1.0 — Validate 1C managed form +# form-validate v1.1 — Validate 1C managed form # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -23,10 +23,12 @@ def main(): sys.stderr.reconfigure(encoding="utf-8") parser = argparse.ArgumentParser(description="Validate 1C managed form", allow_abbrev=False) parser.add_argument("-FormPath", required=True) + parser.add_argument("-Detailed", action="store_true") parser.add_argument("-MaxErrors", type=int, default=30) args = parser.parse_args() form_path = args.FormPath + detailed = args.Detailed max_errors = args.MaxErrors if not os.path.isfile(form_path): @@ -48,22 +50,27 @@ def main(): errors = 0 warnings = 0 + ok_count = 0 stopped = False + output_lines = [] def report_ok(msg): - print(f"[OK] {msg}") + nonlocal ok_count + ok_count += 1 + if detailed: + output_lines.append(f"[OK] {msg}") def report_error(msg): nonlocal errors, stopped errors += 1 - print(f"[ERROR] {msg}") + output_lines.append(f"[ERROR] {msg}") if errors >= max_errors: stopped = True def report_warn(msg): nonlocal warnings warnings += 1 - print(f"[WARN] {msg}") + output_lines.append(f"[WARN] {msg}") # --- Form name from path --- form_name = os.path.splitext(os.path.basename(form_path))[0] @@ -75,8 +82,8 @@ def main(): if form_dir: form_name = os.path.basename(form_dir) - print(f"=== Validation: {form_name} ===") - print() + output_lines.append(f"=== Validation: Form.{form_name} ===") + output_lines.append("") # Early BaseForm detection has_base_form = root.find(f"{{{F_NS}}}BaseForm") is not None @@ -319,8 +326,6 @@ def main(): path_msg = f"{path_msg}, {skip_note}" if path_msg else skip_note if path_errors == 0 and path_msg: report_ok(f"DataPath references: {path_msg}") - elif path_errors == 0: - report_ok("DataPath references: none") # --- Check 6: Button command references --- if not stopped: @@ -355,8 +360,6 @@ def main(): if cmd_errors == 0 and cmd_checked > 0: report_ok(f"Command references: {cmd_checked} buttons checked") - elif cmd_checked == 0: - report_ok("Command references: none") # --- Check 7: Events have handler names --- if not stopped: @@ -396,8 +399,6 @@ def main(): if event_errors == 0 and event_checked > 0: report_ok(f"Event handlers: {event_checked} events checked") - elif event_checked == 0: - report_ok("Event handlers: none") # --- Check 8: Command actions --- if not stopped: @@ -416,8 +417,6 @@ def main(): if action_errors == 0 and action_checked > 0: report_ok(f"Command actions: {action_checked} commands checked") - elif action_checked == 0: - report_ok("Command actions: none") # --- Check 9: MainAttribute count --- if not stopped: @@ -568,18 +567,16 @@ def main(): if call_type_without_base: report_warn("callType attributes found but no BaseForm \u2014 possible incorrect structure") - # --- Summary --- - print() - print("---") - print(f"Total: {len(all_elements)} elements, {len(attr_nodes)} attributes, {len(cmd_nodes)} commands") - - if stopped: - print(f"Stopped after {max_errors} errors. Fix and re-run.") - - if errors == 0 and warnings == 0: - print("All checks passed.") + # --- Finalize --- + checks = ok_count + errors + warnings + if errors == 0 and warnings == 0 and not detailed: + result = f"=== Validation OK: Form.{form_name} ({checks} checks) ===" else: - print(f"Errors: {errors}, Warnings: {warnings}") + output_lines.append("") + output_lines.append(f"=== Result: {errors} errors, {warnings} warnings ({checks} checks) ===") + result = "\n".join(output_lines) + + print(result) if errors > 0: sys.exit(1) diff --git a/.claude/skills/interface-validate/SKILL.md b/.claude/skills/interface-validate/SKILL.md index f126a335..ae0eeb3e 100644 --- a/.claude/skills/interface-validate/SKILL.md +++ b/.claude/skills/interface-validate/SKILL.md @@ -1,7 +1,7 @@ --- name: interface-validate description: Валидация командного интерфейса 1С. Используй после настройки командного интерфейса подсистемы для проверки корректности -argument-hint: [-MaxErrors 30] +argument-hint: [-Detailed] [-MaxErrors 30] allowed-tools: - Bash - Read @@ -10,15 +10,23 @@ allowed-tools: # /interface-validate — валидация CommandInterface.xml -Проверяет XML командного интерфейса из выгрузки конфигурации на структурные ошибки: корневой элемент, допустимые секции, порядок, формат ссылок на команды, дубликаты. +Проверяет XML командного интерфейса на структурные ошибки: корневой элемент, допустимые секции, порядок, формат ссылок на команды, дубликаты. + +## Использование + +``` +/interface-validate +/interface-validate Subsystems/Продажи/Ext/CommandInterface.xml +``` ## Параметры -| Параметр | Обязательный | По умолчанию | Описание | -|-----------|:------------:|--------------|------------------------------------| -| CIPath | да | — | Путь к CommandInterface.xml | -| MaxErrors | нет | 30 | Остановиться после N ошибок | -| OutFile | нет | — | Записать результат в файл (UTF-8 BOM) | +| Параметр | Обяз. | Умолч. | Описание | +|-----------|:-----:|---------|-----------------------------------------| +| CIPath | да | — | Путь к CommandInterface.xml | +| Detailed | нет | — | Показывать [OK] для каждой проверки | +| MaxErrors | нет | 30 | Остановиться после N ошибок | +| OutFile | нет | — | Записать результат в файл (UTF-8 BOM) | ## Команда @@ -44,39 +52,4 @@ powershell.exe -NoProfile -File '.claude/skills/interface-validate/scripts/inter | 12 | GroupsOrder — нет дубликатов | WARN | | 13 | Формат ссылок на команды | WARN | -## Вывод - -``` -=== Validation: CommandInterface (Продажи) === - -[OK] 1. Root structure: CommandInterface, version 2.17, namespace valid -[OK] 2. Child elements: 5 valid sections -[OK] 3. Section order: correct -[OK] 4. No duplicate sections -[OK] 5. CommandsVisibility: 55 entries, all valid -[OK] 6. CommandsVisibility: no duplicates -[OK] 7. CommandsPlacement: 3 entries, all valid -[OK] 8. CommandsOrder: 12 entries, all valid -[OK] 9. SubsystemsOrder: 9 entries, all valid format -[OK] 10. SubsystemsOrder: no duplicates -[OK] 11. GroupsOrder: 7 entries, all valid -[OK] 12. GroupsOrder: no duplicates -[OK] 13. Command reference format: all valid ---- -Errors: 0, Warnings: 0 -``` - -Код возврата: 0 = все проверки пройдены, 1 = есть ошибки. - -## Примеры - -```powershell -# CommandInterface подсистемы -... -CIPath upload/acc_8.3.24/Subsystems/Продажи/Ext/CommandInterface.xml - -# Корневой CommandInterface конфигурации -... -CIPath upload/acc_8.3.24/Ext/CommandInterface.xml - -# С лимитом ошибок -... -CIPath -MaxErrors 10 -``` +Exit code: 0 = OK, 1 = есть ошибки. По умолчанию краткий вывод. `-Detailed` для поштучной детализации. diff --git a/.claude/skills/interface-validate/scripts/interface-validate.ps1 b/.claude/skills/interface-validate/scripts/interface-validate.ps1 index 5340761f..f153cfa9 100644 --- a/.claude/skills/interface-validate/scripts/interface-validate.ps1 +++ b/.claude/skills/interface-validate/scripts/interface-validate.ps1 @@ -1,7 +1,8 @@ -# interface-validate v1.0 — Validate 1C CommandInterface.xml structure +# interface-validate v1.1 — Validate 1C CommandInterface.xml structure # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)][string]$CIPath, + [switch]$Detailed, [int]$MaxErrors = 30, [string]$OutFile ) @@ -33,11 +34,15 @@ if (-not $contextName) { $contextName = "Root" } $script:errors = 0 $script:warnings = 0 $script:stopped = $false +$script:okCount = 0 $script:output = New-Object System.Text.StringBuilder 8192 $script:allCommandNames = @() function Out-Line([string]$msg) { $script:output.AppendLine($msg) | Out-Null } -function Report-OK([string]$msg) { Out-Line "[OK] $msg" } +function Report-OK([string]$msg) { + $script:okCount++ + if ($Detailed) { Out-Line "[OK] $msg" } +} function Report-Error([string]$msg) { $script:errors++ Out-Line "[ERROR] $msg" @@ -358,16 +363,20 @@ if (-not $script:stopped) { $shown = $badRefs[0..([Math]::Min(4, $badRefs.Count - 1))] Report-Warn "13. Command reference format: $($badRefs.Count) unrecognized: $($shown -join ', ')$(if($badRefs.Count -gt 5){' ...'})" } - } else { - Report-OK "13. Command reference format: n/a (no commands)" } } # --- Finalize --- -Out-Line "---" -Out-Line "Errors: $($script:errors), Warnings: $($script:warnings)" +$checks = $script:okCount + $script:errors + $script:warnings + +if ($script:errors -eq 0 -and $script:warnings -eq 0 -and -not $Detailed) { + $result = "=== Validation OK: CommandInterface ($contextName) ($checks checks) ===" +} else { + Out-Line "" + Out-Line "=== Result: $($script:errors) errors, $($script:warnings) warnings ($checks checks) ===" + $result = $script:output.ToString() +} -$result = $script:output.ToString() Write-Host $result if ($OutFile) { diff --git a/.claude/skills/interface-validate/scripts/interface-validate.py b/.claude/skills/interface-validate/scripts/interface-validate.py index 9a56d135..ee81894d 100644 --- a/.claude/skills/interface-validate/scripts/interface-validate.py +++ b/.claude/skills/interface-validate/scripts/interface-validate.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# interface-validate v1.0 — Validate 1C CommandInterface.xml structure +# interface-validate v1.1 — Validate 1C CommandInterface.xml structure # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills """Validates CommandInterface.xml sections, command references, order, duplicates.""" import sys, os, argparse, re @@ -31,18 +31,22 @@ UUID_CMD_PATTERN = re.compile( class Reporter: - def __init__(self, max_errors): + def __init__(self, max_errors, detailed=False): self.errors = 0 self.warnings = 0 + self.ok_count = 0 self.stopped = False self.max_errors = max_errors + self.detailed = detailed self.lines = [] def out(self, msg=''): self.lines.append(msg) def ok(self, msg): - self.lines.append(f'[OK] {msg}') + self.ok_count += 1 + if self.detailed: + self.lines.append(f'[OK] {msg}') def error(self, msg): self.errors += 1 @@ -76,11 +80,13 @@ def main(): description='Validate 1C CommandInterface.xml structure', allow_abbrev=False ) parser.add_argument('-CIPath', dest='CIPath', required=True) + parser.add_argument('-Detailed', action='store_true') parser.add_argument('-MaxErrors', dest='MaxErrors', type=int, default=30) parser.add_argument('-OutFile', dest='OutFile', default='') args = parser.parse_args() ci_path = args.CIPath + detailed = args.Detailed max_errors = args.MaxErrors out_file = args.OutFile @@ -103,7 +109,7 @@ def main(): if not context_name: context_name = 'Root' - r = Reporter(max_errors) + r = Reporter(max_errors, detailed) all_command_names = [] r.out(f'=== Validation: CommandInterface ({context_name}) ===') @@ -210,8 +216,7 @@ def main(): vis_ok = False if vis_ok: r.ok(f'5. CommandsVisibility: {vis_count} entries, all valid') - else: - r.ok('5. CommandsVisibility: not present') + # CommandsVisibility not present — no check needed # --- 6. CommandsVisibility duplicates --- if not r.stopped: @@ -221,8 +226,6 @@ def main(): r.warn(f'6. CommandsVisibility: duplicates: {", ".join(dupes)}') else: r.ok('6. CommandsVisibility: no duplicates') - else: - r.ok('6. CommandsVisibility: no duplicates (empty)') # --- 7. CommandsPlacement --- if not r.stopped: @@ -253,8 +256,7 @@ def main(): r.warn(f"7. CommandsPlacement[{cmd_name}]: Placement='{(placement_el.text or '').strip()}' (expected Auto)") if plc_ok: r.ok(f'7. CommandsPlacement: {plc_count} entries, all valid') - else: - r.ok('7. CommandsPlacement: not present') + # CommandsPlacement not present — no check needed # --- 8. CommandsOrder --- if not r.stopped: @@ -278,8 +280,7 @@ def main(): ord_ok = False if ord_ok: r.ok(f'8. CommandsOrder: {ord_count} entries, all valid') - else: - r.ok('8. CommandsOrder: not present') + # CommandsOrder not present — no check needed # --- 9. SubsystemsOrder format --- sub_names = [] @@ -302,8 +303,7 @@ def main(): sub_ok = False if sub_ok: r.ok(f'9. SubsystemsOrder: {sub_count} entries, all valid format') - else: - r.ok('9. SubsystemsOrder: not present') + # SubsystemsOrder not present — no check needed # --- 10. SubsystemsOrder duplicates --- if not r.stopped: @@ -313,8 +313,6 @@ def main(): r.warn(f'10. SubsystemsOrder: duplicates: {", ".join(dupes)}') else: r.ok('10. SubsystemsOrder: no duplicates') - else: - r.ok('10. SubsystemsOrder: no duplicates (empty)') # --- 11. GroupsOrder entries --- grp_names = [] @@ -334,8 +332,7 @@ def main(): grp_ok = False if grp_ok: r.ok(f'11. GroupsOrder: {grp_count} entries, all valid') - else: - r.ok('11. GroupsOrder: not present') + # GroupsOrder not present — no check needed # --- 12. GroupsOrder duplicates --- if not r.stopped: @@ -345,8 +342,6 @@ def main(): r.warn(f'12. GroupsOrder: duplicates: {", ".join(dupes)}') else: r.ok('12. GroupsOrder: no duplicates') - else: - r.ok('12. GroupsOrder: no duplicates (empty)') # --- 13. Command reference format --- if not r.stopped: @@ -368,14 +363,16 @@ def main(): shown = bad_refs[:5] suffix = ' ...' if len(bad_refs) > 5 else '' r.warn(f'13. Command reference format: {len(bad_refs)} unrecognized: {", ".join(shown)}{suffix}') - else: - r.ok('13. Command reference format: n/a (no commands)') # --- Finalize --- - r.out('---') - r.out(f'Errors: {r.errors}, Warnings: {r.warnings}') + checks = r.ok_count + r.errors + r.warnings + if r.errors == 0 and r.warnings == 0 and not detailed: + result = f'=== Validation OK: CommandInterface ({context_name}) ({checks} checks) ===' + else: + r.out('') + r.out(f'=== Result: {r.errors} errors, {r.warnings} warnings ({checks} checks) ===') + result = '\r\n'.join(r.lines) + '\r\n' - result = r.text() print(result, end='') if out_file: diff --git a/.claude/skills/meta-validate/SKILL.md b/.claude/skills/meta-validate/SKILL.md index 0cbb215b..627ab1e3 100644 --- a/.claude/skills/meta-validate/SKILL.md +++ b/.claude/skills/meta-validate/SKILL.md @@ -1,7 +1,7 @@ --- name: meta-validate description: Валидация объекта метаданных 1С. Используй после создания или модификации объекта конфигурации для проверки корректности -argument-hint: [-MaxErrors 30] — pipe-separated paths for batch +argument-hint: [-Detailed] [-MaxErrors 30] — pipe-separated paths for batch allowed-tools: - Bash - Read @@ -10,26 +10,25 @@ allowed-tools: # /meta-validate — валидация объекта метаданных 1С -Проверяет XML объекта метаданных из выгрузки конфигурации на структурные ошибки: корневую структуру, InternalInfo, свойства, допустимые значения, StandardAttributes, ChildObjects, уникальность имён, табличные части, кросс-свойства, вложенные структуры HTTP/Web-сервисов. +Проверяет XML объекта метаданных из выгрузки конфигурации на структурные ошибки. ## Использование ``` /meta-validate -/meta-validate path1.xml|path2.xml|path3.xml — batch mode +/meta-validate path1.xml|path2.xml — batch mode ``` -## Параметры - -| Параметр | Обязательный | По умолчанию | Описание | -|------------|:------------:|--------------|-------------------------------------------------| -| ObjectPath | да | — | Путь к XML-файлу или каталогу объекта. Несколько путей через `\|` для batch | -| MaxErrors | нет | 30 | Остановиться после N ошибок (per object) | -| OutFile | нет | — | Записать результат в файл (UTF-8 BOM) | - `ObjectPath` авторезолв: если указана директория — ищет `/.xml`. -**Batch mode**: при нескольких путях через `|` каждый объект валидируется отдельно, в конце выводится сводка `=== Batch: N objects, X passed, Y failed ===`. +## Параметры + +| Параметр | Обяз. | Умолч. | Описание | +|------------|:-----:|---------|-------------------------------------------------| +| ObjectPath | да | — | Путь к XML-файлу или каталогу. Через `\|` для batch | +| Detailed | нет | — | Показывать [OK] для каждой проверки | +| MaxErrors | нет | 30 | Остановиться после N ошибок (per object) | +| OutFile | нет | — | Записать результат в файл (UTF-8 BOM) | ## Команда @@ -45,7 +44,7 @@ powershell.exe -NoProfile -File .claude/skills/meta-validate/scripts/meta-valida **Сервисные:** CommonModule, ScheduledJob, EventSubscription, HTTPService, WebService **Прочие:** Constant, DocumentJournal, DefinedType -## Выполняемые проверки +## Проверки | # | Проверка | Серьёзность | |----|------------------------------------------|--------------| @@ -56,63 +55,13 @@ powershell.exe -NoProfile -File .claude/skills/meta-validate/scripts/meta-valida | 5 | StandardAttributes | ERROR / WARN | | 6 | ChildObjects — допустимые элементы | ERROR | | 7 | Attributes/Dimensions/Resources — UUID, Name, Type | ERROR | +| 7b | Reserved attribute names | WARN | | 8 | Уникальность имён | ERROR | | 9 | TabularSections — внутренняя структура | ERROR / WARN | | 10 | Кросс-свойства | ERROR / WARN | | 11 | HTTPService/WebService — вложенная структура | ERROR | +| 12 | Forbidden properties per type | ERROR | +| 13 | Method reference (Handler/MethodName) | ERROR / WARN | +| 14 | DocumentJournal Columns | ERROR | -## Вывод - -``` -=== Validation: Catalog.Номенклатура === - -[OK] 1. Root structure: MetaDataObject/Catalog, version 2.17 -[OK] 2. InternalInfo: 5 GeneratedType (Object, Ref, Selection, List, Manager) -[OK] 3. Properties: Name="Номенклатура", Synonym present -[OK] 4. Property values: 12 enum properties checked -[ERROR] 5. StandardAttributes: missing "PredefinedDataName" -[OK] 6. ChildObjects types: Attribute(15), TabularSection(3), Form(4) -[OK] 7. Attributes/Dimensions: all valid -[WARN] 8. Name uniqueness: duplicate attribute "Комментарий" at positions 5, 12 -[OK] 9. TabularSections: 3 sections, structure valid -[OK] 10. Cross-property consistency -[OK] 11. N/A (not HTTPService/WebService) ---- -Errors: 1, Warnings: 1 -``` - -Код возврата: 0 = все проверки пройдены, 1 = есть ошибки. - -## Примеры - -```powershell -# Справочник из выгрузки конфигурации -... -ObjectPath upload/acc_8.3.24/Catalogs/Банки/Банки.xml - -# Авторезолв из директории -... -ObjectPath upload/acc_8.3.24/Documents/АвансовыйОтчет - -# С лимитом ошибок -... -ObjectPath Catalogs/Номенклатура.xml -MaxErrors 10 - -# С записью в файл -... -ObjectPath Catalogs/Номенклатура.xml -OutFile result.txt - -# Batch: несколько объектов через | -... -ObjectPath "Catalogs/Банки.xml|Documents/Заказ.xml|Enums/ВидДоговора.xml" -``` - -## Верификация - -``` -/meta-compile — генерация XML -/meta-validate //.xml — проверка результата -/meta-info //.xml — визуальная сводка -``` - -## Когда использовать - -- **После `/meta-compile`**: проверить корректность сгенерированного XML -- **После ручного редактирования**: убедиться что структура не нарушена -- **После merge/импорта**: выявить конфликты и битые ссылки -- **При отладке**: найти структурные ошибки до сборки EPF +Exit code: 0 = OK, 1 = есть ошибки. По умолчанию краткий вывод. `-Detailed` для поштучной детализации. diff --git a/.claude/skills/meta-validate/scripts/meta-validate.ps1 b/.claude/skills/meta-validate/scripts/meta-validate.ps1 index 80b76b65..d30f210a 100644 --- a/.claude/skills/meta-validate/scripts/meta-validate.ps1 +++ b/.claude/skills/meta-validate/scripts/meta-validate.ps1 @@ -1,9 +1,11 @@ -# meta-validate v1.1 — Validate 1C metadata object structure +# meta-validate v1.2 — Validate 1C metadata object structure # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)] [string]$ObjectPath, + [switch]$Detailed, + [int]$MaxErrors = 30, [string]$OutFile @@ -19,7 +21,7 @@ if ($pathList.Count -gt 1) { $batchOk = 0 $batchFail = 0 foreach ($singlePath in $pathList) { - $callArgs = @{ ObjectPath = $singlePath; MaxErrors = $MaxErrors } + $callArgs = @{ ObjectPath = $singlePath; MaxErrors = $MaxErrors; Verbose = $Detailed } if ($OutFile) { $baseName = [System.IO.Path]::GetFileNameWithoutExtension($OutFile) $ext = [System.IO.Path]::GetExtension($OutFile) @@ -96,6 +98,7 @@ for ($depth = 0; $depth -lt 4; $depth++) { $script:errors = 0 $script:warnings = 0 +$script:okCount = 0 $script:stopped = $false $script:output = New-Object System.Text.StringBuilder 8192 @@ -106,7 +109,8 @@ function Out-Line { function Report-OK { param([string]$msg) - Out-Line "[OK] $msg" + $script:okCount++ + if ($Detailed) { Out-Line "[OK] $msg" } } function Report-Error { @@ -125,10 +129,14 @@ function Report-Warn { } $finalize = { - Out-Line "" - Out-Line "=== Result: $($script:errors) errors, $($script:warnings) warnings ===" - - $result = $script:output.ToString() + $checks = $script:okCount + $script:errors + $script:warnings + if ($script:errors -eq 0 -and $script:warnings -eq 0 -and -not $Detailed) { + $result = "=== Validation OK: $mdType.$objName ($checks checks) ===" + } else { + Out-Line "" + Out-Line "=== Result: $($script:errors) errors, $($script:warnings) warnings ($checks checks) ===" + $result = $script:output.ToString() + } Write-Host $result if ($OutFile) { @@ -449,8 +457,6 @@ if ($typesWithoutInternalInfo -contains $mdType) { Report-OK "2. InternalInfo: $($genTypes.Count) GeneratedType ($catList)" } } -} else { - Report-OK "2. InternalInfo: N/A for $mdType" } if ($script:stopped) { & $finalize; exit 1 } @@ -569,8 +575,6 @@ if ($typesWithStdAttrs -contains $mdType) { Report-OK "5. StandardAttributes: $($stdAttrs.Count) entries" } } -} else { - Report-OK "5. StandardAttributes: N/A for $mdType" } if ($script:stopped) { & $finalize; exit 1 } @@ -690,8 +694,6 @@ if ($childObjNode) { } elseif ($check7Count -eq 0) { Report-OK "7. Child elements: none to check" } -} else { - Report-OK "7. Child elements: N/A (no ChildObjects)" } if ($script:stopped) { & $finalize; exit 1 } @@ -725,8 +727,6 @@ if ($childObjNode) { if ($check7bOk) { Report-OK "7b. Reserved attribute names: no conflicts" } -} else { - Report-OK "7b. Reserved attribute names: N/A" } if ($script:stopped) { & $finalize; exit 1 } @@ -814,8 +814,6 @@ if ($childObjNode) { if ($check8Ok) { Report-OK "8. Name uniqueness: all names unique" } -} else { - Report-OK "8. Name uniqueness: N/A" } if ($script:stopped) { & $finalize; exit 1 } @@ -901,8 +899,6 @@ if ($childObjNode) { } else { Report-OK "9. TabularSections: none present" } -} else { - Report-OK "9. TabularSections: N/A" } if ($script:stopped) { & $finalize; exit 1 } @@ -1160,8 +1156,6 @@ if ($mdType -eq "HTTPService" -and $childObjNode) { if ($check11Ok) { Report-OK "11. WebService: $($operations.Count) operation(s), $paramCount parameter(s)" } -} else { - Report-OK "11. HTTPService/WebService: N/A" } if ($script:stopped) { & $finalize; exit 1 } @@ -1181,8 +1175,6 @@ if ($propsNode -and $forbiddenProperties.ContainsKey($mdType)) { if ($check12Ok) { Report-OK "12. Forbidden properties: none found" } -} else { - Report-OK "12. Forbidden properties: N/A" } if ($script:stopped) { & $finalize; exit 1 } @@ -1245,8 +1237,6 @@ if ($propsNode -and $mdType -in @("EventSubscription","ScheduledJob") -and $scri if ($check13Ok) { Report-OK "13. Method reference: $propLabel = '$methodRef'" } -} else { - Report-OK "13. Method reference: N/A" } if ($script:stopped) { & $finalize; exit 1 } @@ -1283,8 +1273,6 @@ if ($mdType -eq "DocumentJournal" -and $childObjNode) { } elseif ($colCount -eq 0) { Report-OK "14. DocumentJournal Columns: none" } -} else { - Report-OK "14. DocumentJournal Columns: N/A" } # --- Final output --- diff --git a/.claude/skills/meta-validate/scripts/meta-validate.py b/.claude/skills/meta-validate/scripts/meta-validate.py index 6f6fb2ef..c55ef8ae 100644 --- a/.claude/skills/meta-validate/scripts/meta-validate.py +++ b/.claude/skills/meta-validate/scripts/meta-validate.py @@ -1,4 +1,4 @@ -# meta-validate v1.1 — Validate 1C metadata object structure (Python port) +# meta-validate v1.2 — Validate 1C metadata object structure (Python port) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse import os @@ -15,10 +15,12 @@ sys.stderr.reconfigure(encoding="utf-8") parser = argparse.ArgumentParser(allow_abbrev=False) parser.add_argument("-ObjectPath", required=True) +parser.add_argument("-Detailed", action="store_true") parser.add_argument("-MaxErrors", type=int, default=30) parser.add_argument("-OutFile", default="") args = parser.parse_args() +detailed = args.Detailed max_errors = args.MaxErrors out_file = args.OutFile @@ -30,6 +32,8 @@ if len(path_list) > 1: batch_fail = 0 for single_path in path_list: cmd = [sys.executable, __file__, "-ObjectPath", single_path, "-MaxErrors", str(max_errors)] + if detailed: + cmd.append("-Detailed") if out_file: base, ext = os.path.splitext(out_file) obj_leaf = os.path.splitext(os.path.basename(single_path))[0] @@ -98,6 +102,7 @@ for _ in range(4): errors = 0 warnings = 0 +ok_count = 0 stopped = False output_lines = [] @@ -107,7 +112,10 @@ def out_line(msg): def report_ok(msg): - out_line(f"[OK] {msg}") + global ok_count + ok_count += 1 + if detailed: + out_line(f"[OK] {msg}") def report_error(msg): @@ -125,9 +133,13 @@ def report_warn(msg): def finalize(): - out_line("") - out_line(f"=== Result: {errors} errors, {warnings} warnings ===") - result = "\n".join(output_lines) + checks = ok_count + errors + warnings + if errors == 0 and warnings == 0 and not detailed: + result = f"=== Validation OK: {md_type}.{obj_name} ({checks} checks) ===" + else: + out_line("") + out_line(f"=== Result: {errors} errors, {warnings} warnings ({checks} checks) ===") + result = "\n".join(output_lines) print(result) if out_file: with open(out_file, "w", encoding="utf-8-sig") as f: @@ -453,8 +465,6 @@ elif md_type in generated_type_categories: if check2_ok: cat_list = ", ".join(sorted(found_categories)) report_ok(f"2. InternalInfo: {len(gen_types)} GeneratedType ({cat_list})") -else: - report_ok(f"2. InternalInfo: N/A for {md_type}") if stopped: finalize() @@ -560,8 +570,6 @@ if md_type in types_with_std_attrs: if check5_ok: report_ok(f"5. StandardAttributes: {len(std_attrs)} entries") -else: - report_ok(f"5. StandardAttributes: N/A for {md_type}") if stopped: finalize() @@ -663,8 +671,6 @@ if child_obj_node is not None: report_ok(f"7. Child elements: {check7_count} items checked (UUID, Name, Type)") elif check7_count == 0: report_ok("7. Child elements: none to check") -else: - report_ok("7. Child elements: N/A (no ChildObjects)") if stopped: finalize() @@ -694,8 +700,6 @@ if child_obj_node is not None: check7b_ok = False if check7b_ok: report_ok("7b. Reserved attribute names: no conflicts") -else: - report_ok("7b. Reserved attribute names: N/A") if stopped: finalize() @@ -776,8 +780,6 @@ if child_obj_node is not None: if check8_ok: report_ok("8. Name uniqueness: all names unique") -else: - report_ok("8. Name uniqueness: N/A") if stopped: finalize() @@ -854,8 +856,6 @@ if child_obj_node is not None: report_ok(f"9. TabularSections: {ts_count} sections, structure valid") else: report_ok("9. TabularSections: none present") -else: - report_ok("9. TabularSections: N/A") if stopped: finalize() @@ -1083,8 +1083,6 @@ elif md_type == "WebService" and child_obj_node is not None: if check11_ok: report_ok(f"11. WebService: {len(operations)} operation(s), {param_count} parameter(s)") -else: - report_ok("11. HTTPService/WebService: N/A") if stopped: finalize() @@ -1102,8 +1100,6 @@ if props_node is not None and md_type in forbidden_properties: check12_ok = False if check12_ok: report_ok("12. Forbidden properties: none found") -else: - report_ok("12. Forbidden properties: N/A") if stopped: finalize() @@ -1161,8 +1157,6 @@ if props_node is not None and md_type in ("EventSubscription", "ScheduledJob") a if check13_ok: report_ok(f"13. Method reference: {prop_label} = '{method_ref}'") -else: - report_ok("13. Method reference: N/A") if stopped: finalize() @@ -1197,8 +1191,6 @@ if md_type == "DocumentJournal" and child_obj_node is not None: report_ok(f"14. DocumentJournal Columns: {col_count} column(s), all have References") elif col_count == 0: report_ok("14. DocumentJournal Columns: none") -else: - report_ok("14. DocumentJournal Columns: N/A") # ── Final output ────────────────────────────────────────────── diff --git a/.claude/skills/mxl-validate/SKILL.md b/.claude/skills/mxl-validate/SKILL.md index c8107d6e..e3b6a639 100644 --- a/.claude/skills/mxl-validate/SKILL.md +++ b/.claude/skills/mxl-validate/SKILL.md @@ -1,33 +1,34 @@ --- name: mxl-validate description: Валидация макета табличного документа (MXL). Используй после создания или модификации макета для проверки корректности -argument-hint: или +argument-hint: [-Detailed] [-MaxErrors 20] allowed-tools: - Bash - Read - Glob --- -# /mxl-validate — Валидатор макета +# /mxl-validate — валидация макета табличного документа (MXL) -Проверяет Template.xml на структурные ошибки, которые платформа 1С может молча проигнорировать (с возможной потерей данных или повреждением макета). +Проверяет Template.xml на структурные ошибки: индексы, ссылки на палитры, диапазоны именованных областей и объединений. ## Использование ``` /mxl-validate -/mxl-validate +/mxl-validate -ProcessorName "МояОбработка" -TemplateName "Макет" ``` ## Параметры -| Параметр | Обязательный | По умолчанию | Описание | -|---------------|:------------:|--------------|------------------------------------------| -| TemplatePath | нет | — | Прямой путь к Template.xml | -| ProcessorName | нет | — | Имя обработки (альтернатива пути) | -| TemplateName | нет | — | Имя макета (альтернатива пути) | -| SrcDir | нет | `src` | Каталог исходников | -| MaxErrors | нет | 20 | Остановиться после N ошибок | +| Параметр | Обяз. | Умолч. | Описание | +|---------------|:-----:|---------|--------------------------------------------| +| TemplatePath | нет | — | Прямой путь к Template.xml | +| ProcessorName | нет | — | Имя обработки (альтернатива пути) | +| TemplateName | нет | — | Имя макета (альтернатива пути) | +| SrcDir | нет | `src` | Каталог исходников | +| Detailed | нет | — | Показывать [OK] для каждой проверки | +| MaxErrors | нет | 20 | Остановиться после N ошибок | Укажите либо `-TemplatePath`, либо оба `-ProcessorName` и `-TemplateName`. @@ -37,12 +38,7 @@ allowed-tools: powershell.exe -NoProfile -File .claude/skills/mxl-validate/scripts/mxl-validate.ps1 -TemplatePath "<путь>" ``` -Или по имени обработки/макета: -```powershell -powershell.exe -NoProfile -File .claude/skills/mxl-validate/scripts/mxl-validate.ps1 -ProcessorName "<Имя>" -TemplateName "<Макет>" [-SrcDir "<каталог>"] -``` - -## Выполняемые проверки +## Проверки | # | Проверка | Серьёзность | |---|---|---| @@ -59,27 +55,4 @@ powershell.exe -NoProfile -File .claude/skills/mxl-validate/scripts/mxl-validate | 11 | Индексы линий границ в форматах в пределах палитры линий | ERROR | | 12 | `pictureIndex` рисунков ссылается на существующую картинку | ERROR | -## Вывод - -``` -=== Validation: ИмяМакета === - -[OK] height (40) >= max row index + 1 (40), rowsItem count=34 -[OK] Font refs: max=3, palette size=4 -[ERROR] Row 15: cell format index 38 > format palette size (37) -[OK] Column indices: max in default set=32, default column count=33 ---- -Errors: 1, Warnings: 0 -``` - -Код возврата: 0 = все проверки пройдены, 1 = есть ошибки. - -## Когда использовать - -- **После генерации макета**: запустить валидатор для выявления структурных ошибок до сборки EPF -- **После редактирования Template.xml**: убедиться, что индексы и ссылки остались валидными -- **При ошибках**: исправить найденные проблемы и перезапустить до полного прохождения - -## Защита от переполнения - -Останавливается после 20 ошибок по умолчанию (настраивается через `-MaxErrors`). Итоговая строка с количеством ошибок/предупреждений выводится всегда. +Exit code: 0 = OK, 1 = есть ошибки. По умолчанию краткий вывод. `-Detailed` для поштучной детализации. diff --git a/.claude/skills/mxl-validate/scripts/mxl-validate.ps1 b/.claude/skills/mxl-validate/scripts/mxl-validate.ps1 index b44a125e..ba35f8fb 100644 --- a/.claude/skills/mxl-validate/scripts/mxl-validate.ps1 +++ b/.claude/skills/mxl-validate/scripts/mxl-validate.ps1 @@ -1,10 +1,11 @@ -# mxl-validate v1.0 — Validate 1C spreadsheet +# mxl-validate v1.1 — Validate 1C spreadsheet # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [string]$TemplatePath, [string]$ProcessorName, [string]$TemplateName, [string]$SrcDir = "src", + [switch]$Detailed, [int]$MaxErrors = 20 ) @@ -44,10 +45,12 @@ $root = $xmlDoc.DocumentElement $errors = 0 $warnings = 0 $stopped = $false +$script:okCount = 0 function Report-OK { param([string]$msg) - Write-Host "[OK] $msg" + $script:okCount++ + if ($Detailed) { Write-Host "[OK] $msg" } } function Report-Error { @@ -66,8 +69,10 @@ function Report-Warn { } $templateName = [System.IO.Path]::GetFileName([System.IO.Path]::GetDirectoryName([System.IO.Path]::GetDirectoryName($TemplatePath))) -Write-Host "=== Validation: $templateName ===" -Write-Host "" +if ($Detailed) { + Write-Host "=== Validation: $templateName ===" + Write-Host "" +} # --- Collect palettes --- @@ -376,18 +381,18 @@ foreach ($drawing in $root.SelectNodes("d:drawing", $nsMgr)) { # --- Summary --- -# :finish label equivalent -Write-Host "" -Write-Host "---" +$checks = $script:okCount + $errors + $warnings -if ($stopped) { - Write-Host "Stopped after $MaxErrors errors. Fix and re-run." -} - -if ($errors -eq 0 -and $warnings -eq 0) { - Write-Host "All checks passed." +if ($errors -eq 0 -and $warnings -eq 0 -and -not $Detailed) { + Write-Host "=== Validation OK: Template.$templateName ($checks checks) ===" } else { - Write-Host "Errors: $errors, Warnings: $warnings" + Write-Host "" + + if ($stopped) { + Write-Host "Stopped after $MaxErrors errors. Fix and re-run." + } + + Write-Host "=== Result: $errors errors, $warnings warnings ($checks checks) ===" } if ($errors -gt 0) { diff --git a/.claude/skills/mxl-validate/scripts/mxl-validate.py b/.claude/skills/mxl-validate/scripts/mxl-validate.py index 5af52c16..3cecfbbc 100644 --- a/.claude/skills/mxl-validate/scripts/mxl-validate.py +++ b/.claude/skills/mxl-validate/scripts/mxl-validate.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# mxl-validate v1.0 — Validate 1C spreadsheet document Template.xml +# mxl-validate v1.1 — Validate 1C spreadsheet document Template.xml # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills """Validates spreadsheet Template.xml: height, palette refs, column/row indices, areas, merges.""" import sys, os, argparse @@ -17,24 +17,29 @@ NS = { class Reporter: - def __init__(self, max_errors): + def __init__(self, max_errors, detailed=False): self.errors = 0 self.warnings = 0 + self.ok_count = 0 self.stopped = False self.max_errors = max_errors + self.detailed = detailed + self.lines = [] def ok(self, msg): - print(f'[OK] {msg}') + self.ok_count += 1 + if self.detailed: + self.lines.append(f'[OK] {msg}') def error(self, msg): self.errors += 1 - print(f'[ERROR] {msg}') + self.lines.append(f'[ERROR] {msg}') if self.errors >= self.max_errors: self.stopped = True def warn(self, msg): self.warnings += 1 - print(f'[WARN] {msg}') + self.lines.append(f'[WARN] {msg}') def int_text(node): @@ -54,6 +59,7 @@ def main(): parser.add_argument('-ProcessorName', dest='ProcessorName', default='') parser.add_argument('-TemplateName', dest='TemplateName', default='') parser.add_argument('-SrcDir', dest='SrcDir', default='src') + parser.add_argument('-Detailed', action='store_true') parser.add_argument('-MaxErrors', dest='MaxErrors', type=int, default=20) args = parser.parse_args() @@ -61,6 +67,7 @@ def main(): processor_name = args.ProcessorName template_name_arg = args.TemplateName src_dir = args.SrcDir + detailed = args.Detailed max_errors = args.MaxErrors # --- Resolve template path --- @@ -85,13 +92,13 @@ def main(): xml_doc = etree.parse(resolved_path, xml_parser) root = xml_doc.getroot() - r = Reporter(max_errors) + r = Reporter(max_errors, detailed) # Derive template name from path: .../Templates//Ext/Template.xml # Go up 2 levels from Template.xml -> Ext -> template_display_name = os.path.basename(os.path.dirname(os.path.dirname(resolved_path))) - print(f'=== Validation: {template_display_name} ===') - print() + r.lines.append(f'=== Validation: Template.{template_display_name} ===') + r.lines.append('') # --- Collect palettes --- line_nodes = root.findall(f'{{{NS_D}}}line') @@ -176,8 +183,7 @@ def main(): r.error(f'Font index {max_font_ref} exceeds palette size ({font_count})') elif max_font_ref > 0: r.error(f'Font index {max_font_ref} referenced but no fonts defined') - else: - r.ok('No font references') + # No font references — no check needed # --- Check 11: line/border indices in formats --- if line_count > 0: @@ -187,8 +193,7 @@ def main(): r.error(f'Line index {max_line_ref} exceeds palette size ({line_count})') elif max_line_ref > 0: r.error(f'Line index {max_line_ref} referenced but no lines defined') - else: - r.ok('No line/border references') + # No line/border references — no check needed # --- Check 3, 4, 5, 6: row/cell checks --- max_cell_format_ref = 0 @@ -348,17 +353,16 @@ def main(): draw_id = draw_id_node.text if draw_id_node is not None else '?' r.error(f'Drawing id={draw_id}: pictureIndex={pic_idx} > picture count ({picture_count})') - # --- Summary --- - print() - print('---') - - if r.stopped: - print(f'Stopped after {max_errors} errors. Fix and re-run.') - - if r.errors == 0 and r.warnings == 0: - print('All checks passed.') + # --- Finalize --- + checks = r.ok_count + r.errors + r.warnings + if r.errors == 0 and r.warnings == 0 and not detailed: + result = f'=== Validation OK: Template.{template_display_name} ({checks} checks) ===' else: - print(f'Errors: {r.errors}, Warnings: {r.warnings}') + r.lines.append('') + r.lines.append(f'=== Result: {r.errors} errors, {r.warnings} warnings ({checks} checks) ===') + result = '\n'.join(r.lines) + + print(result) sys.exit(1 if r.errors > 0 else 0) diff --git a/.claude/skills/role-validate/SKILL.md b/.claude/skills/role-validate/SKILL.md index d2b2299d..9f604df6 100644 --- a/.claude/skills/role-validate/SKILL.md +++ b/.claude/skills/role-validate/SKILL.md @@ -1,7 +1,7 @@ --- name: role-validate description: Валидация роли 1С. Используй после создания или модификации роли для проверки корректности -argument-hint: +argument-hint: [-Detailed] [-MetadataPath ] allowed-tools: - Bash - Read @@ -14,92 +14,38 @@ allowed-tools: ## Использование ``` -/role-validate [MetadataPath] +/role-validate +/role-validate Roles/МояРоль/Ext/Rights.xml Roles/МояРоль.xml ``` -## Запуск скрипта +## Параметры -```powershell -powershell.exe -NoProfile -File .claude/skills/role-validate/scripts/role-validate.ps1 -RightsPath [-MetadataPath ] [-OutFile ] -``` - -### Параметры - -| Параметр | Обязательный | Описание | -|----------|:------------:|----------| -| `-RightsPath` | да | Путь к `Rights.xml` роли | -| `-MetadataPath` | нет | Путь к метаданным роли (`Roles/ИмяРоли.xml`) | -| `-OutFile` | нет | Записать результат в файл (UTF-8 BOM). Без этого — вывод в консоль | +| Параметр | Обяз. | Умолч. | Описание | +|--------------|:-----:|---------|-------------------------------------------------| +| RightsPath | да | — | Путь к `Rights.xml` роли | +| MetadataPath | нет | — | Путь к метаданным роли (`Roles/ИмяРоли.xml`) | +| Detailed | нет | — | Показывать [OK] для каждой проверки | +| OutFile | нет | — | Записать результат в файл (UTF-8 BOM) | **Важно:** Для кириллических путей используй `-OutFile` и читай результат через Read tool. +## Команда + +```powershell +powershell.exe -NoProfile -File .claude/skills/role-validate/scripts/role-validate.ps1 -RightsPath [-MetadataPath ] +``` + ## Проверки -### Rights.xml -1. XML well-formed — парсинг без ошибок -2. Корневой элемент `` с namespace `http://v8.1c.ru/8.2/roles` -3. Три глобальных флага: `setForNewObjects`, `setForAttributesByDefault`, `independentRightsOfChildObjects` -4. Для каждого ``: - - `` не пуст - - Тип объекта распознан (Catalog, Document, InformationRegister и т.д.) - - Каждое `` имеет `` и `` (`true`/`false`) - - Имя права валидно для данного типа объекта (с подсказкой при опечатке) -5. Вложенные объекты (3+ сегмента через `.`): допустимы только View, Edit (или Use для IntegrationServiceChannel) -6. RLS ``: `` не пуст -7. Шаблоны ``: `` и `` не пусты +| # | Проверка | Серьёзность | +|---|----------|-------------| +| 1 | XML well-formed — парсинг без ошибок | ERROR | +| 2 | Корневой элемент `` с namespace `http://v8.1c.ru/8.2/roles` | ERROR | +| 3 | Три глобальных флага: setForNewObjects, setForAttributesByDefault, independentRightsOfChildObjects | ERROR | +| 4 | Объекты: name не пуст, тип распознан, права валидны для типа (с подсказкой при опечатке) | ERROR/WARN | +| 5 | Вложенные объекты (3+ сегмента): допустимы только View, Edit (или Use для IntegrationServiceChannel) | ERROR | +| 6 | RLS ``: condition не пуст | ERROR | +| 7 | Шаблоны ``: name и condition не пусты | ERROR | +| 8 | Метаданные (если MetadataPath): UUID, Name, Synonym | ERROR/WARN | -### Метаданные (опционально) -- Элемент `` найден -- UUID в корректном формате -- `` не пуст -- `` присутствует - -## Формат вывода - -``` -Validating: Roles/МояРоль/Ext/Rights.xml - OK XML well-formed - OK Root element: with correct namespace - OK 3 global flags present - WARN Document.Реализация: unknown right 'Rea'. Did you mean: Read? - OK 12 objects, 45 rights - OK 2 RLS restrictions - OK 1 templates: ДляОбъекта - - OK Metadata: UUID valid (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) - OK Metadata: Name = МояРоль - OK Metadata: Synonym present ---- -Result: 0 error(s), 1 warning(s) -``` - -### Уровни сообщений - -| Маркер | Значение | -|--------|----------| -| `OK` | Проверка пройдена | -| `WARN` | Предупреждение (неизвестный тип объекта, подозрительное имя права) | -| `ERR` | Ошибка (невалидный XML, отсутствие обязательных элементов) | - -Код возврата: `0` — без ошибок, `1` — есть ошибки. - -## Примеры - -### Только Rights.xml - -``` -/role-validate upload/acc_8.3.20/Roles/БазовыеПраваБП/Ext/Rights.xml -``` - -### С проверкой метаданных - -``` -/role-validate Roles/МояРоль/Ext/Rights.xml Roles/МояРоль.xml -``` - -### Верификация после /role-compile - -``` -/role-compile role.json Roles/ -/role-validate Roles/МояРоль/Ext/Rights.xml Roles/МояРоль.xml -``` +Exit code: 0 = OK, 1 = есть ошибки. По умолчанию краткий вывод. `-Detailed` для поштучной детализации. diff --git a/.claude/skills/role-validate/scripts/role-validate.ps1 b/.claude/skills/role-validate/scripts/role-validate.ps1 index be8022b5..f3ba2cdd 100644 --- a/.claude/skills/role-validate/scripts/role-validate.ps1 +++ b/.claude/skills/role-validate/scripts/role-validate.ps1 @@ -1,4 +1,4 @@ -# role-validate v1.0 — Validate 1C role structure +# role-validate v1.1 — Validate 1C role structure # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)] @@ -6,7 +6,11 @@ param( [string]$MetadataPath, - [string]$OutFile + [string]$OutFile, + + [switch]$Detailed, + + [int]$MaxErrors = 30 ) $ErrorActionPreference = "Stop" @@ -132,25 +136,36 @@ $script:commandRights = @("View") # --- 2. Output helpers --- -$script:lines = @() $script:errors = 0 $script:warnings = 0 +$script:okCount = 0 +$script:stopped = $false +$script:output = New-Object System.Text.StringBuilder 8192 -function Out-OK { +function Out-Line { param([string]$msg) - $script:lines += " OK $msg" + $script:output.AppendLine($msg) | Out-Null } -function Out-WARN { +function Report-OK { param([string]$msg) - $script:warnings++ - $script:lines += " WARN $msg" + $script:okCount++ + if ($Detailed) { Out-Line "[OK] $msg" } } -function Out-ERR { +function Report-Error { param([string]$msg) $script:errors++ - $script:lines += " ERR $msg" + Out-Line "[ERROR] $msg" + if ($script:errors -ge $MaxErrors) { + $script:stopped = $true + } +} + +function Report-Warn { + param([string]$msg) + $script:warnings++ + Out-Line "[WARN] $msg" } function Get-ObjectType { @@ -176,18 +191,17 @@ function Find-Similar { # --- 3. Validate Rights.xml --- -$script:lines += "Validating: $RightsPath" - if (-not (Test-Path $RightsPath)) { - Out-ERR "File not found: $RightsPath" - $script:lines += "---" - $script:lines += "Result: $($script:errors) error(s), $($script:warnings) warning(s)" - $output = $script:lines -join "`n" + Report-Error "File not found: $RightsPath" + $result = $script:output.ToString() + Write-Host $result if ($OutFile) { - $enc = New-Object System.Text.UTF8Encoding($true) - [System.IO.File]::WriteAllText($OutFile, $output, $enc) - } else { - Write-Host $output + $outPath = if ([System.IO.Path]::IsPathRooted($OutFile)) { $OutFile } else { Join-Path (Get-Location) $OutFile } + $outDir = [System.IO.Path]::GetDirectoryName($outPath) + if (-not (Test-Path $outDir)) { New-Item -ItemType Directory -Path $outDir -Force | Out-Null } + $utf8Bom = New-Object System.Text.UTF8Encoding $true + [System.IO.File]::WriteAllText($outPath, $result, $utf8Bom) + Write-Host "Written to: $outPath" } exit 1 } @@ -195,17 +209,18 @@ if (-not (Test-Path $RightsPath)) { # 3a. Parse XML try { [xml]$xml = Get-Content -Path $RightsPath -Encoding UTF8 - Out-OK "XML well-formed" + Report-OK "XML well-formed" } catch { - Out-ERR "XML parse error: $($_.Exception.Message)" - $script:lines += "---" - $script:lines += "Result: $($script:errors) error(s), $($script:warnings) warning(s)" - $output = $script:lines -join "`n" + Report-Error "XML parse error: $($_.Exception.Message)" + $result = $script:output.ToString() + Write-Host $result if ($OutFile) { - $enc = New-Object System.Text.UTF8Encoding($true) - [System.IO.File]::WriteAllText($OutFile, $output, $enc) - } else { - Write-Host $output + $outPath = if ([System.IO.Path]::IsPathRooted($OutFile)) { $OutFile } else { Join-Path (Get-Location) $OutFile } + $outDir = [System.IO.Path]::GetDirectoryName($outPath) + if (-not (Test-Path $outDir)) { New-Item -ItemType Directory -Path $outDir -Force | Out-Null } + $utf8Bom = New-Object System.Text.UTF8Encoding $true + [System.IO.File]::WriteAllText($outPath, $result, $utf8Bom) + Write-Host "Written to: $outPath" } exit 1 } @@ -215,11 +230,11 @@ $rightsNs = "http://v8.1c.ru/8.2/roles" # 3b. Check root element if ($root.LocalName -ne "Rights") { - Out-ERR "Root element is '$($root.LocalName)', expected 'Rights'" + Report-Error "Root element is '$($root.LocalName)', expected 'Rights'" } elseif ($root.NamespaceURI -ne $rightsNs) { - Out-WARN "Namespace is '$($root.NamespaceURI)', expected '$rightsNs'" + Report-Warn "Namespace is '$($root.NamespaceURI)', expected '$rightsNs'" } else { - Out-OK "Root element: with correct namespace" + Report-OK "Root element: with correct namespace" } # 3c. Global flags @@ -230,15 +245,15 @@ foreach ($fn in $flagNames) { if ($node.Count -gt 0) { $val = $node[0].InnerText if ($val -ne "true" -and $val -ne "false") { - Out-WARN "$fn = '$val' (expected 'true' or 'false')" + Report-Warn "$fn = '$val' (expected 'true' or 'false')" } $flagsFound++ } else { - Out-WARN "Missing global flag: $fn" + Report-Warn "Missing global flag: $fn" } } if ($flagsFound -eq 3) { - Out-OK "3 global flags present" + Report-OK "3 global flags present" } # 3d. Objects @@ -257,7 +272,7 @@ foreach ($obj in $objects) { } if (-not $objName) { - Out-ERR "Object without " + Report-Error "Object without " continue } @@ -266,7 +281,7 @@ foreach ($obj in $objects) { # Check object type is known if (-not $isNested -and -not $script:knownRights.ContainsKey($objectType)) { - Out-WARN "${objName}: unknown object type '$objectType'" + Report-Warn "${objName}: unknown object type '$objectType'" } # Check rights @@ -289,18 +304,18 @@ foreach ($obj in $objects) { if ($rcc.LocalName -eq "condition") { $condNode = $rcc } } if (-not $condNode -or -not $condNode.InnerText) { - Out-WARN "${objName}: RLS condition for '$rName' is empty" + Report-Warn "${objName}: RLS condition for '$rName' is empty" } } } if (-not $rName) { - Out-ERR "${objName}: without " + Report-Error "${objName}: without " continue } if ($rValue -ne "true" -and $rValue -ne "false") { - Out-ERR "${objName}: right '$rName' has invalid value '$rValue'" + Report-Error "${objName}: right '$rName' has invalid value '$rValue'" continue } @@ -310,15 +325,15 @@ foreach ($obj in $objects) { if ($isNested) { if ($objName -match '\.Command\.') { if ($rName -notin $script:commandRights) { - Out-WARN "${objName}: '$rName' not valid for commands (only: View)" + Report-Warn "${objName}: '$rName' not valid for commands (only: View)" } } elseif ($objName -match '\.IntegrationServiceChannel\.') { if ($rName -notin $script:channelRights) { - Out-WARN "${objName}: '$rName' not valid for channels (only: Use)" + Report-Warn "${objName}: '$rName' not valid for channels (only: Use)" } } else { if ($rName -notin $script:nestedRights) { - Out-WARN "${objName}: '$rName' not valid for nested objects (only: View, Edit)" + Report-Warn "${objName}: '$rName' not valid for nested objects (only: View, Edit)" } } } elseif ($script:knownRights.ContainsKey($objectType)) { @@ -326,15 +341,15 @@ foreach ($obj in $objects) { if ($rName -notin $validRights) { $similar = Find-Similar -needle $rName -haystack $validRights $sugStr = if ($similar.Count -gt 0) { " Did you mean: $($similar -join ', ')?" } else { "" } - Out-WARN "${objName}: unknown right '$rName'.$sugStr" + Report-Warn "${objName}: unknown right '$rName'.$sugStr" } } } } -Out-OK "$objCount objects, $rightCount rights" +Report-OK "$objCount objects, $rightCount rights" if ($rlsCount -gt 0) { - Out-OK "$rlsCount RLS restrictions" + Report-OK "$rlsCount RLS restrictions" } # 3e. Templates @@ -349,56 +364,56 @@ if ($templates.Count -gt 0) { if ($child.LocalName -eq "condition") { $tCond = $child.InnerText } } if (-not $tName) { - Out-WARN "Restriction template without " + Report-Warn "Restriction template without " } else { $parenIdx = $tName.IndexOf("(") $shortName = if ($parenIdx -gt 0) { $tName.Substring(0, $parenIdx) } else { $tName } $tplNames += $shortName } if (-not $tCond) { - Out-WARN "Template '$tName': empty " + Report-Warn "Template '$tName': empty " } } - Out-OK "$($templates.Count) templates: $($tplNames -join ', ')" + Report-OK "$($templates.Count) templates: $($tplNames -join ', ')" } # --- 4. Validate metadata (optional) --- if ($MetadataPath) { - $script:lines += "" + Out-Line "" if (-not (Test-Path $MetadataPath)) { - Out-ERR "Metadata file not found: $MetadataPath" + Report-Error "Metadata file not found: $MetadataPath" } else { try { [xml]$metaXml = Get-Content -Path $MetadataPath -Encoding UTF8 $roleNode = $metaXml.DocumentElement.SelectSingleNode("//*[local-name()='Role']") if (-not $roleNode) { - Out-ERR "Metadata: element not found" + Report-Error "Metadata: element not found" } else { $uuid = $roleNode.GetAttribute("uuid") if ($uuid -match '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$') { - Out-OK "Metadata: UUID valid ($uuid)" + Report-OK "Metadata: UUID valid ($uuid)" } else { - Out-ERR "Metadata: invalid UUID format '$uuid'" + Report-Error "Metadata: invalid UUID format '$uuid'" } $nameNode = $roleNode.SelectSingleNode(".//*[local-name()='Name']") if ($nameNode -and $nameNode.InnerText) { - Out-OK "Metadata: Name = $($nameNode.InnerText)" + Report-OK "Metadata: Name = $($nameNode.InnerText)" } else { - Out-ERR "Metadata: is empty or missing" + Report-Error "Metadata: is empty or missing" } $synNode = $roleNode.SelectSingleNode(".//*[local-name()='Synonym']") if ($synNode -and $synNode.InnerXml) { - Out-OK "Metadata: Synonym present" + Report-OK "Metadata: Synonym present" } else { - Out-WARN "Metadata: is empty" + Report-Warn "Metadata: is empty" } } } catch { - Out-ERR "Metadata XML parse error: $($_.Exception.Message)" + Report-Error "Metadata XML parse error: $($_.Exception.Message)" } } } @@ -425,7 +440,7 @@ if ($MetadataPath -and (Test-Path $MetadataPath)) { } if (Test-Path $configXmlPath2) { - $script:lines += "" + Out-Line "" try { [xml]$cfgXml = Get-Content -Path $configXmlPath2 -Encoding UTF8 $cfgNs = New-Object System.Xml.XmlNamespaceManager($cfgXml.NameTable) @@ -441,22 +456,30 @@ if (Test-Path $configXmlPath2) { } } if ($found) { - Out-OK "Configuration.xml: $inferredRoleName registered" + Report-OK "Configuration.xml: $inferredRoleName registered" } else { - Out-WARN "Configuration.xml: $inferredRoleName NOT found in ChildObjects" + Report-Warn "Configuration.xml: $inferredRoleName NOT found in ChildObjects" } } } catch { - Out-WARN "Configuration.xml: parse error — $($_.Exception.Message)" + Report-Warn "Configuration.xml: parse error — $($_.Exception.Message)" } } # --- 6. Summary --- -$script:lines += "---" -$script:lines += "Result: $($script:errors) error(s), $($script:warnings) warning(s)" +# Insert header +$script:output.Insert(0, "=== Validation: Role.$inferredRoleName ===$([Environment]::NewLine)") | Out-Null -$output = $script:lines -join "`n" +$checks = $script:okCount + $script:errors + $script:warnings +if ($script:errors -eq 0 -and $script:warnings -eq 0 -and -not $Detailed) { + $result = "=== Validation OK: Role.$inferredRoleName ($checks checks) ===" +} else { + Out-Line "" + Out-Line "=== Result: $($script:errors) errors, $($script:warnings) warnings ($checks checks) ===" + $result = $script:output.ToString() +} +Write-Host $result if ($OutFile) { $outPath = if ([System.IO.Path]::IsPathRooted($OutFile)) { $OutFile } else { Join-Path (Get-Location) $OutFile } @@ -464,11 +487,9 @@ if ($OutFile) { if (-not (Test-Path $outDir)) { New-Item -ItemType Directory -Path $outDir -Force | Out-Null } - $enc = New-Object System.Text.UTF8Encoding($true) - [System.IO.File]::WriteAllText($outPath, $output, $enc) - Write-Host "[OK] Validation result written to: $outPath" -} else { - Write-Host $output + $utf8Bom = New-Object System.Text.UTF8Encoding $true + [System.IO.File]::WriteAllText($outPath, $result, $utf8Bom) + Write-Host "Written to: $outPath" } if ($script:errors -gt 0) { exit 1 } else { exit 0 } diff --git a/.claude/skills/role-validate/scripts/role-validate.py b/.claude/skills/role-validate/scripts/role-validate.py index 4755fbca..688b1652 100644 --- a/.claude/skills/role-validate/scripts/role-validate.py +++ b/.claude/skills/role-validate/scripts/role-validate.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# role-validate v1.0 — Validate 1C role Rights.xml structure +# role-validate v1.1 — Validate 1C role Rights.xml structure # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills """Validates role Rights.xml: root element, global flags, objects, rights, RLS, templates.""" import sys, os, argparse, re @@ -182,6 +182,8 @@ def main(): parser.add_argument('-RightsPath', dest='RightsPath', required=True) parser.add_argument('-MetadataPath', dest='MetadataPath', default='') parser.add_argument('-OutFile', dest='OutFile', default='') + parser.add_argument('-Detailed', dest='Detailed', action='store_true') + parser.add_argument('-MaxErrors', dest='MaxErrors', type=int, default=30) args = parser.parse_args() rights_path = args.RightsPath @@ -195,41 +197,45 @@ def main(): lines = [] errors = 0 warnings = 0 + ok_count = 0 + stopped = False - def out_ok(msg): - lines.append(f' OK {msg}') + def report_ok(msg): + nonlocal ok_count + ok_count += 1 + if args.Detailed: + lines.append(f'[OK] {msg}') - def out_warn(msg): + def report_warn(msg): nonlocal warnings warnings += 1 - lines.append(f' WARN {msg}') + lines.append(f'[WARN] {msg}') - def out_err(msg): - nonlocal errors + def report_error(msg): + nonlocal errors, stopped errors += 1 - lines.append(f' ERR {msg}') + lines.append(f'[ERROR] {msg}') + if errors >= args.MaxErrors: + stopped = True # --- 3. Validate Rights.xml --- - lines.append(f'Validating: {rights_path}') - def finalize(): - lines.append('---') - lines.append(f'Result: {errors} error(s), {warnings} warning(s)') - output = '\n'.join(lines) + def write_output(text): if out_file: out_path = out_file if os.path.isabs(out_file) else os.path.join(os.getcwd(), out_file) out_dir = os.path.dirname(out_path) if out_dir and not os.path.exists(out_dir): os.makedirs(out_dir, exist_ok=True) with open(out_path, 'w', encoding='utf-8-sig', newline='') as f: - f.write(output) - print(f'[OK] Validation result written to: {out_path}') + f.write(text) + print(f'Written to: {out_path}') else: - print(output) + print(text) if not os.path.exists(rights_path): - out_err(f'File not found: {rights_path}') - finalize() + report_error(f'File not found: {rights_path}') + result = '\n'.join(lines) + write_output(result) sys.exit(1) # 3a. Parse XML @@ -237,10 +243,11 @@ def main(): try: xml_parser = etree.XMLParser(remove_blank_text=False) xml_doc = etree.parse(rights_path, xml_parser) - out_ok('XML well-formed') + report_ok('XML well-formed') except etree.XMLSyntaxError as e: - out_err(f'XML parse error: {e}') - finalize() + report_error(f'XML parse error: {e}') + result = '\n'.join(lines) + write_output(result) sys.exit(1) root = xml_doc.getroot() @@ -249,11 +256,11 @@ def main(): # 3b. Check root element if root_local != 'Rights': - out_err(f"Root element is '{root_local}', expected 'Rights'") + report_error(f"Root element is '{root_local}', expected 'Rights'") elif root_ns != RIGHTS_NS: - out_warn(f"Namespace is '{root_ns}', expected '{RIGHTS_NS}'") + report_warn(f"Namespace is '{root_ns}', expected '{RIGHTS_NS}'") else: - out_ok('Root element: with correct namespace') + report_ok('Root element: with correct namespace') # 3c. Global flags flag_names = ['setForNewObjects', 'setForAttributesByDefault', 'independentRightsOfChildObjects'] @@ -263,12 +270,12 @@ def main(): if len(nodes) > 0: val = nodes[0].text or '' if val not in ('true', 'false'): - out_warn(f"{fn} = '{val}' (expected 'true' or 'false')") + report_warn(f"{fn} = '{val}' (expected 'true' or 'false')") flags_found += 1 else: - out_warn(f'Missing global flag: {fn}') + report_warn(f'Missing global flag: {fn}') if flags_found == 3: - out_ok('3 global flags present') + report_ok('3 global flags present') # 3d. Objects objects = root.findall(f'{{{RIGHTS_NS}}}object') @@ -286,7 +293,7 @@ def main(): break if not obj_name: - out_err('Object without ') + report_error('Object without ') continue object_type = get_object_type(obj_name) @@ -294,7 +301,7 @@ def main(): # Check object type is known if not is_nested and object_type not in KNOWN_RIGHTS: - out_warn(f"{obj_name}: unknown object type '{object_type}'") + report_warn(f"{obj_name}: unknown object type '{object_type}'") # Check rights for child in obj: @@ -325,14 +332,14 @@ def main(): # Check condition not empty cond_node = get_child_el(rc, 'condition', RIGHTS_NS) if cond_node is None or not (cond_node.text or ''): - out_warn(f"{obj_name}: RLS condition for '{r_name}' is empty") + report_warn(f"{obj_name}: RLS condition for '{r_name}' is empty") if not r_name: - out_err(f'{obj_name}: without ') + report_error(f'{obj_name}: without ') continue if r_value not in ('true', 'false'): - out_err(f"{obj_name}: right '{r_name}' has invalid value '{r_value}'") + report_error(f"{obj_name}: right '{r_name}' has invalid value '{r_value}'") continue right_count += 1 @@ -341,23 +348,23 @@ def main(): if is_nested: if '.Command.' in obj_name: if r_name not in COMMAND_RIGHTS: - out_warn(f"{obj_name}: '{r_name}' not valid for commands (only: View)") + report_warn(f"{obj_name}: '{r_name}' not valid for commands (only: View)") elif '.IntegrationServiceChannel.' in obj_name: if r_name not in CHANNEL_RIGHTS: - out_warn(f"{obj_name}: '{r_name}' not valid for channels (only: Use)") + report_warn(f"{obj_name}: '{r_name}' not valid for channels (only: Use)") else: if r_name not in NESTED_RIGHTS: - out_warn(f"{obj_name}: '{r_name}' not valid for nested objects (only: View, Edit)") + report_warn(f"{obj_name}: '{r_name}' not valid for nested objects (only: View, Edit)") elif object_type in KNOWN_RIGHTS: valid_rights = KNOWN_RIGHTS[object_type] if r_name not in valid_rights: similar = find_similar(r_name, valid_rights) sug_str = f' Did you mean: {", ".join(similar)}?' if similar else '' - out_warn(f"{obj_name}: unknown right '{r_name}'.{sug_str}") + report_warn(f"{obj_name}: unknown right '{r_name}'.{sug_str}") - out_ok(f'{obj_count} objects, {right_count} rights') + report_ok(f'{obj_count} objects, {right_count} rights') if rls_count > 0: - out_ok(f'{rls_count} RLS restrictions') + report_ok(f'{rls_count} RLS restrictions') # 3e. Templates templates = root.findall(f'{{{RIGHTS_NS}}}restrictionTemplate') @@ -378,14 +385,14 @@ def main(): elif local == 'condition': t_cond = child.text or '' if not t_name: - out_warn('Restriction template without ') + report_warn('Restriction template without ') else: paren_idx = t_name.find('(') short_name = t_name[:paren_idx] if paren_idx > 0 else t_name tpl_names.append(short_name) if not t_cond: - out_warn(f"Template '{t_name}': empty ") - out_ok(f'{len(templates)} templates: {", ".join(tpl_names)}') + report_warn(f"Template '{t_name}': empty ") + report_ok(f'{len(templates)} templates: {", ".join(tpl_names)}') # --- 4. Validate metadata (optional) --- inferred_role_name = '' @@ -396,7 +403,7 @@ def main(): metadata_path = os.path.join(os.getcwd(), metadata_path) if not os.path.exists(metadata_path): - out_err(f'Metadata file not found: {metadata_path}') + report_error(f'Metadata file not found: {metadata_path}') else: try: meta_parser = etree.XMLParser(remove_blank_text=False) @@ -410,13 +417,13 @@ def main(): break if role_node is None: - out_err('Metadata: element not found') + report_error('Metadata: element not found') else: uuid_val = role_node.get('uuid', '') if GUID_PATTERN.match(uuid_val): - out_ok(f'Metadata: UUID valid ({uuid_val})') + report_ok(f'Metadata: UUID valid ({uuid_val})') else: - out_err(f"Metadata: invalid UUID format '{uuid_val}'") + report_error(f"Metadata: invalid UUID format '{uuid_val}'") # Find Name name_node = None @@ -426,10 +433,10 @@ def main(): break if name_node is not None and name_node.text: - out_ok(f'Metadata: Name = {name_node.text}') + report_ok(f'Metadata: Name = {name_node.text}') inferred_role_name = name_node.text else: - out_err('Metadata: is empty or missing') + report_error('Metadata: is empty or missing') # Find Synonym syn_node = None @@ -439,11 +446,11 @@ def main(): break if syn_node is not None and len(syn_node) > 0: - out_ok('Metadata: Synonym present') + report_ok('Metadata: Synonym present') else: - out_warn('Metadata: is empty') + report_warn('Metadata: is empty') except etree.XMLSyntaxError as e: - out_err(f'Metadata XML parse error: {e}') + report_error(f'Metadata XML parse error: {e}') # --- 5. Check registration in Configuration.xml --- resolved_rights = os.path.abspath(rights_path) @@ -487,14 +494,25 @@ def main(): found = True break if found: - out_ok(f'Configuration.xml: {inferred_role_name} registered') + report_ok(f'Configuration.xml: {inferred_role_name} registered') else: - out_warn(f'Configuration.xml: {inferred_role_name} NOT found in ChildObjects') + report_warn(f'Configuration.xml: {inferred_role_name} NOT found in ChildObjects') except etree.XMLSyntaxError as e: - out_warn(f'Configuration.xml: parse error \u2014 {e}') + report_warn(f'Configuration.xml: parse error \u2014 {e}') # --- 6. Summary --- - finalize() + + # Insert header at position 0 + lines.insert(0, f'=== Validation: Role.{inferred_role_name} ===') + + checks = ok_count + errors + warnings + if errors == 0 and warnings == 0 and not args.Detailed: + result = f'=== Validation OK: Role.{inferred_role_name} ({checks} checks) ===' + else: + lines.append('') + lines.append(f'=== Result: {errors} errors, {warnings} warnings ({checks} checks) ===') + result = '\n'.join(lines) + write_output(result) sys.exit(1 if errors > 0 else 0) diff --git a/.claude/skills/skd-validate/SKILL.md b/.claude/skills/skd-validate/SKILL.md index 6d3f73d4..6be77895 100644 --- a/.claude/skills/skd-validate/SKILL.md +++ b/.claude/skills/skd-validate/SKILL.md @@ -1,7 +1,7 @@ --- name: skd-validate description: Валидация схемы компоновки данных 1С (СКД). Используй после создания или модификации СКД для проверки корректности -argument-hint: [-MaxErrors 20] +argument-hint: [-Detailed] [-MaxErrors 20] allowed-tools: - Bash - Read @@ -12,13 +12,25 @@ allowed-tools: Проверяет структурную корректность Template.xml схемы компоновки данных. Выявляет ошибки формата, битые ссылки, дубликаты имён. -## Параметры и команда +## Использование -| Параметр | Описание | -|----------|----------| -| `TemplatePath` | Путь к Template.xml или каталогу макета (авто-резолв в `Ext/Template.xml`) | -| `MaxErrors` | Макс. ошибок до остановки (по умолчанию 20) | -| `OutFile` | Записать результат в файл | +``` +/skd-validate +/skd-validate path/to/Ext/Template.xml +``` + +`TemplatePath` авторезолв: если указан каталог макета — ищет `Ext/Template.xml`. + +## Параметры + +| Параметр | Обяз. | Умолч. | Описание | +|--------------|:-----:|---------|---------------------------------------------------------| +| TemplatePath | да | — | Путь к Template.xml или каталогу макета | +| Detailed | нет | — | Показывать [OK] для каждой проверки | +| MaxErrors | нет | 20 | Остановиться после N ошибок | +| OutFile | нет | — | Записать результат в файл | + +## Команда ```powershell powershell.exe -NoProfile -File .claude/skills/skd-validate/scripts/skd-validate.ps1 -TemplatePath "<путь>" @@ -41,34 +53,4 @@ powershell.exe -NoProfile -File .claude/skills/skd-validate/scripts/skd-validate | **Variants** | Наличие, name не пуст, settings element присутствует | | **Settings** | selection/filter/order ссылаются на известные поля, comparisonType валиден, structure items типизированы | -## Коды выхода - -| Код | Значение | -|-----|----------| -| 0 | Ошибок нет (могут быть предупреждения) | -| 1 | Есть ошибки | - -## Пример вывода - -``` -=== Validation: Template.xml === - -[OK] XML parsed successfully -[OK] Root element: DataCompositionSchema -[OK] Default namespace correct -[OK] 1 dataSource(s) found, names unique -[OK] 1 dataSet(s) found, names unique -[OK] DataSet "НаборДанных1": 2 fields, dataPath unique -[OK] 1 totalField(s): dataPath and expression present -[OK] 1 settingsVariant(s) found - -=== Result: 0 errors, 0 warnings === -``` - -## Верификация - -``` -/skd-compile — генерация XML -/skd-validate — проверка результата -/skd-info — визуальная сводка -``` +Exit code: 0 = OK, 1 = есть ошибки. По умолчанию краткий вывод. `-Detailed` для поштучной детализации. diff --git a/.claude/skills/skd-validate/scripts/skd-validate.ps1 b/.claude/skills/skd-validate/scripts/skd-validate.ps1 index 2baa86c0..7a91890f 100644 --- a/.claude/skills/skd-validate/scripts/skd-validate.ps1 +++ b/.claude/skills/skd-validate/scripts/skd-validate.ps1 @@ -1,9 +1,11 @@ -# skd-validate v1.0 — Validate 1C DCS structure +# skd-validate v1.1 — Validate 1C DCS structure # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)] [string]$TemplatePath, + [switch]$Detailed, + [int]$MaxErrors = 20, [string]$OutFile @@ -33,6 +35,7 @@ $fileName = [System.IO.Path]::GetFileName($resolvedPath) $script:errors = 0 $script:warnings = 0 +$script:okCount = 0 $script:stopped = $false $script:output = New-Object System.Text.StringBuilder 4096 @@ -43,7 +46,8 @@ function Out-Line { function Report-OK { param([string]$msg) - Out-Line "[OK] $msg" + $script:okCount++ + if ($Detailed) { Out-Line "[OK] $msg" } } function Report-Error { @@ -62,10 +66,14 @@ function Report-Warn { } $finalize = { - Out-Line "" - Out-Line "=== Result: $($script:errors) errors, $($script:warnings) warnings ===" - - $result = $script:output.ToString() + $checks = $script:okCount + $script:errors + $script:warnings + if ($script:errors -eq 0 -and $script:warnings -eq 0 -and -not $Detailed) { + $result = "=== Validation OK: $fileName ($checks checks) ===" + } else { + Out-Line "" + Out-Line "=== Result: $($script:errors) errors, $($script:warnings) warnings ($checks checks) ===" + $result = $script:output.ToString() + } Write-Host $result if ($OutFile) { diff --git a/.claude/skills/skd-validate/scripts/skd-validate.py b/.claude/skills/skd-validate/scripts/skd-validate.py index 171d926b..3b76d3f3 100644 --- a/.claude/skills/skd-validate/scripts/skd-validate.py +++ b/.claude/skills/skd-validate/scripts/skd-validate.py @@ -1,4 +1,4 @@ -# skd-validate v1.0 — Validate 1C DCS structure (Python port) +# skd-validate v1.1 — Validate 1C DCS structure (Python port) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse import os @@ -13,11 +13,13 @@ sys.stderr.reconfigure(encoding="utf-8") parser = argparse.ArgumentParser(allow_abbrev=False) parser.add_argument("-TemplatePath", required=True) +parser.add_argument("-Detailed", action="store_true") parser.add_argument("-MaxErrors", type=int, default=20) parser.add_argument("-OutFile", default="") args = parser.parse_args() template_path = args.TemplatePath +detailed = args.Detailed max_errors = args.MaxErrors out_file = args.OutFile @@ -39,6 +41,7 @@ file_name = os.path.basename(resolved_path) errors = 0 warnings = 0 +ok_count = 0 stopped = False output_lines = [] @@ -48,7 +51,10 @@ def out_line(msg): def report_ok(msg): - out_line(f"[OK] {msg}") + global ok_count + ok_count += 1 + if detailed: + out_line(f"[OK] {msg}") def report_error(msg): @@ -66,9 +72,13 @@ def report_warn(msg): def finalize(): - out_line("") - out_line(f"=== Result: {errors} errors, {warnings} warnings ===") - result = "\n".join(output_lines) + checks = ok_count + errors + warnings + if errors == 0 and warnings == 0 and not detailed: + result = f"=== Validation OK: {file_name} ({checks} checks) ===" + else: + out_line("") + out_line(f"=== Result: {errors} errors, {warnings} warnings ({checks} checks) ===") + result = "\n".join(output_lines) print(result) if out_file: with open(out_file, "w", encoding="utf-8-sig") as f: diff --git a/.claude/skills/subsystem-validate/SKILL.md b/.claude/skills/subsystem-validate/SKILL.md index 6267c8e1..75673663 100644 --- a/.claude/skills/subsystem-validate/SKILL.md +++ b/.claude/skills/subsystem-validate/SKILL.md @@ -1,7 +1,7 @@ --- name: subsystem-validate description: Валидация подсистемы 1С. Используй после создания или модификации подсистемы для проверки корректности -argument-hint: [-MaxErrors 30] +argument-hint: [-Detailed] [-MaxErrors 30] allowed-tools: - Bash - Read @@ -12,13 +12,23 @@ allowed-tools: Проверяет структурную корректность XML-файла подсистемы из выгрузки конфигурации. -## Параметры и команда +## Использование -| Параметр | Описание | -|----------|----------| -| `SubsystemPath` | Путь к XML-файлу подсистемы | -| `MaxErrors` | Максимум ошибок до остановки (по умолчанию 30) | -| `OutFile` | Записать результат в файл | +``` +/subsystem-validate +/subsystem-validate Subsystems/Продажи.xml +``` + +## Параметры + +| Параметр | Обяз. | Умолч. | Описание | +|---------------|:-----:|---------|--------------------------------------------| +| SubsystemPath | да | — | Путь к XML-файлу подсистемы | +| Detailed | нет | — | Показывать [OK] для каждой проверки | +| MaxErrors | нет | 30 | Остановиться после N ошибок | +| OutFile | нет | — | Записать результат в файл | + +## Команда ```powershell powershell.exe -NoProfile -File '.claude/skills/subsystem-validate/scripts/subsystem-validate.ps1' -SubsystemPath '<путь>' @@ -26,16 +36,20 @@ powershell.exe -NoProfile -File '.claude/skills/subsystem-validate/scripts/subsy ## Проверки (13) -1. XML well-formedness + root structure (MetaDataObject/Subsystem) -2. Properties — 9 обязательных свойств -3. Name — непустой, валидный идентификатор -4. Synonym — непустой (хотя бы один v8:item) -5. Булевы свойства — содержат true/false -6. Content — формат xr:Item, xsi:type -7. Content — нет дубликатов -8. ChildObjects — элементы непустые -9. ChildObjects — нет дубликатов -10. ChildObjects → файлы существуют -11. CommandInterface.xml — well-formedness -12. Picture — формат ссылки -13. UseOneCommand=true → ровно 1 элемент в Content +| # | Проверка | Серьёзность | +|---|----------|-------------| +| 1 | XML well-formedness + root structure (MetaDataObject/Subsystem) | ERROR | +| 2 | Properties — 9 обязательных свойств | ERROR | +| 3 | Name — непустой, валидный идентификатор | ERROR | +| 4 | Synonym — непустой (хотя бы один v8:item) | WARN | +| 5 | Булевы свойства — содержат true/false | ERROR | +| 6 | Content — формат xr:Item, xsi:type | ERROR | +| 7 | Content — нет дубликатов | WARN | +| 8 | ChildObjects — элементы непустые | ERROR | +| 9 | ChildObjects — нет дубликатов | WARN | +| 10 | ChildObjects → файлы существуют | WARN | +| 11 | CommandInterface.xml — well-formedness | ERROR | +| 12 | Picture — формат ссылки | ERROR | +| 13 | UseOneCommand=true → ровно 1 элемент в Content | ERROR | + +Exit code: 0 = OK, 1 = есть ошибки. По умолчанию краткий вывод. `-Detailed` для поштучной детализации. diff --git a/.claude/skills/subsystem-validate/scripts/subsystem-validate.ps1 b/.claude/skills/subsystem-validate/scripts/subsystem-validate.ps1 index ddd37f5b..7245d66c 100644 --- a/.claude/skills/subsystem-validate/scripts/subsystem-validate.ps1 +++ b/.claude/skills/subsystem-validate/scripts/subsystem-validate.ps1 @@ -1,7 +1,8 @@ -# subsystem-validate v1.0 — Validate 1C subsystem XML structure +# subsystem-validate v1.1 — Validate 1C subsystem XML structure # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)][string]$SubsystemPath, + [switch]$Detailed, [int]$MaxErrors = 30, [string]$OutFile ) @@ -43,10 +44,14 @@ $resolvedPath = (Resolve-Path $SubsystemPath).Path $script:errors = 0 $script:warnings = 0 $script:stopped = $false +$script:okCount = 0 $script:output = New-Object System.Text.StringBuilder 8192 function Out-Line([string]$msg) { $script:output.AppendLine($msg) | Out-Null } -function Report-OK([string]$msg) { Out-Line "[OK] $msg" } +function Report-OK([string]$msg) { + $script:okCount++ + if ($Detailed) { Out-Line "[OK] $msg" } +} function Report-Error([string]$msg) { $script:errors++ Out-Line "[ERROR] $msg" @@ -120,8 +125,6 @@ if (-not $script:stopped) { # --- 3. Name --- $nameEl = $props.SelectSingleNode("md:Name", $ns) $subName = if ($nameEl) { $nameEl.InnerText.Trim() } else { "" } - Out-Line "" - Out-Line "=== Validation: Subsystem.$subName ===" # Re-insert header at position 0 $headerLine = "=== Validation: Subsystem.$subName ===" $script:output.Insert(0, "$headerLine`r`n`r`n") | Out-Null @@ -256,8 +259,6 @@ if (-not $script:stopped) { } else { Report-Warn "10. ChildObjects files: missing: $($missingFiles -join ', ')" } - } else { - Report-OK "10. ChildObjects files: n/a (no children)" } # --- 11. CommandInterface.xml --- @@ -307,10 +308,16 @@ if (-not $script:stopped) { } # --- Finalize --- -Out-Line "---" -Out-Line "Errors: $($script:errors), Warnings: $($script:warnings)" +$checks = $script:okCount + $script:errors + $script:warnings + +if ($script:errors -eq 0 -and $script:warnings -eq 0 -and -not $Detailed) { + $result = "=== Validation OK: Subsystem.$subName ($checks checks) ===" +} else { + Out-Line "" + Out-Line "=== Result: $($script:errors) errors, $($script:warnings) warnings ($checks checks) ===" + $result = $script:output.ToString() +} -$result = $script:output.ToString() Write-Host $result if ($OutFile) { diff --git a/.claude/skills/subsystem-validate/scripts/subsystem-validate.py b/.claude/skills/subsystem-validate/scripts/subsystem-validate.py index dd5c80d0..1db00fbe 100644 --- a/.claude/skills/subsystem-validate/scripts/subsystem-validate.py +++ b/.claude/skills/subsystem-validate/scripts/subsystem-validate.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# subsystem-validate v1.0 — Validate 1C subsystem XML structure +# subsystem-validate v1.1 — Validate 1C subsystem XML structure # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills """Validates subsystem XML file structure, properties, content items, child objects.""" import sys, os, argparse, re @@ -22,18 +22,22 @@ IDENT_PATTERN = re.compile( class Reporter: - def __init__(self, max_errors): + def __init__(self, max_errors, detailed=False): self.errors = 0 self.warnings = 0 + self.ok_count = 0 self.stopped = False self.max_errors = max_errors + self.detailed = detailed self.lines = [] def out(self, msg=''): self.lines.append(msg) def ok(self, msg): - self.lines.append(f'[OK] {msg}') + self.ok_count += 1 + if self.detailed: + self.lines.append(f'[OK] {msg}') def error(self, msg): self.errors += 1 @@ -67,11 +71,13 @@ def main(): description='Validate 1C subsystem XML structure', allow_abbrev=False ) parser.add_argument('-SubsystemPath', dest='SubsystemPath', required=True) + parser.add_argument('-Detailed', action='store_true') parser.add_argument('-MaxErrors', dest='MaxErrors', type=int, default=30) parser.add_argument('-OutFile', dest='OutFile', default='') args = parser.parse_args() subsystem_path = args.SubsystemPath + detailed = args.Detailed max_errors = args.MaxErrors out_file = args.OutFile @@ -105,7 +111,7 @@ def main(): sys.exit(1) resolved_path = os.path.abspath(subsystem_path) - r = Reporter(max_errors) + r = Reporter(max_errors, detailed) # --- 1. XML well-formedness + root structure --- xml_doc = None @@ -240,8 +246,6 @@ def main(): r.warn(f'7. Content: duplicates found: {", ".join(dupes)}') else: r.ok('7. Content: no duplicates') - else: - r.ok('7. Content: no duplicates (empty)') # --- 8. ChildObjects entries non-empty --- child_objs = sub.find('md:ChildObjects', NS) @@ -272,8 +276,6 @@ def main(): r.error(f'9. ChildObjects: duplicates: {", ".join(dupes)}') else: r.ok('9. ChildObjects: no duplicates') - else: - r.ok('9. ChildObjects: no duplicates (empty)') # --- 10. ChildObjects files exist --- if len(child_names) > 0: @@ -289,8 +291,6 @@ def main(): r.ok(f'10. ChildObjects files: all {len(child_names)} files exist') else: r.warn(f'10. ChildObjects files: missing: {", ".join(missing_files)}') - else: - r.ok('10. ChildObjects files: n/a (no children)') # --- 11. CommandInterface.xml --- parent_dir2 = os.path.dirname(resolved_path) @@ -331,10 +331,14 @@ def main(): r.ok('13. UseOneCommand: false (no constraint)') # --- Finalize --- - r.out('---') - r.out(f'Errors: {r.errors}, Warnings: {r.warnings}') + checks = r.ok_count + r.errors + r.warnings + if r.errors == 0 and r.warnings == 0 and not detailed: + result = f'=== Validation OK: Subsystem.{sub_name} ({checks} checks) ===' + else: + r.out('') + r.out(f'=== Result: {r.errors} errors, {r.warnings} warnings ({checks} checks) ===') + result = '\r\n'.join(r.lines) + '\r\n' - result = r.text() print(result, end='') if out_file: