From 422e3973811db0cc7015c87cb0cd0e1764e9dda0 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Mon, 9 Mar 2026 18:14:44 +0300 Subject: [PATCH 1/7] 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: From b2a2534b5a8ef75f9e066831de306c21fb122a15 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Mon, 9 Mar 2026 18:41:38 +0300 Subject: [PATCH 2/7] feat(validate): auto-resolve directory paths, fix SKILL.md placeholders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Scripts now accept directory paths (e.g. Forms/ИмяФормы) and auto-resolve to the target XML file. Silent fallbacks handle missing Ext/ level and descriptor-to-file resolution. SKILL.md: concrete placeholders, unified quotes, auto-resolve notes, role-validate MaxErrors in params. Co-Authored-By: Claude Opus 4.6 --- .claude/skills/cfe-validate/SKILL.md | 2 +- .claude/skills/epf-validate/SKILL.md | 2 +- .claude/skills/form-validate/SKILL.md | 4 +++- .../form-validate/scripts/form-validate.ps1 | 21 ++++++++++++++++ .../form-validate/scripts/form-validate.py | 21 ++++++++++++++++ .claude/skills/interface-validate/SKILL.md | 4 +++- .../scripts/interface-validate.ps1 | 12 ++++++++++ .../scripts/interface-validate.py | 11 +++++++++ .claude/skills/meta-validate/SKILL.md | 2 +- .claude/skills/mxl-validate/SKILL.md | 4 +++- .../mxl-validate/scripts/mxl-validate.ps1 | 20 ++++++++++++++++ .../mxl-validate/scripts/mxl-validate.py | 18 ++++++++++++++ .claude/skills/role-validate/SKILL.md | 7 ++++-- .../role-validate/scripts/role-validate.ps1 | 17 +++++++++++++ .../role-validate/scripts/role-validate.py | 11 +++++++++ .claude/skills/skd-validate/SKILL.md | 4 +++- .../skd-validate/scripts/skd-validate.ps1 | 24 +++++++++++++++---- .../skd-validate/scripts/skd-validate.py | 21 ++++++++++++---- .claude/skills/subsystem-validate/SKILL.md | 4 +++- 19 files changed, 191 insertions(+), 18 deletions(-) diff --git a/.claude/skills/cfe-validate/SKILL.md b/.claude/skills/cfe-validate/SKILL.md index 4bb2856b..96a5d9ff 100644 --- a/.claude/skills/cfe-validate/SKILL.md +++ b/.claude/skills/cfe-validate/SKILL.md @@ -33,7 +33,7 @@ allowed-tools: ## Команда ```powershell -powershell.exe -NoProfile -File .claude/skills/cfe-validate/scripts/cfe-validate.ps1 -ExtensionPath src +powershell.exe -NoProfile -File .claude/skills/cfe-validate/scripts/cfe-validate.ps1 -ExtensionPath "<путь>" ``` ## Проверки (9 шагов) diff --git a/.claude/skills/epf-validate/SKILL.md b/.claude/skills/epf-validate/SKILL.md index 116e15d8..fa8439ff 100644 --- a/.claude/skills/epf-validate/SKILL.md +++ b/.claude/skills/epf-validate/SKILL.md @@ -33,7 +33,7 @@ allowed-tools: ## Команда ```powershell -powershell.exe -NoProfile -File .claude/skills/epf-validate/scripts/epf-validate.ps1 -ObjectPath "<путь>" +powershell.exe -NoProfile -File .claude/skills/epf-validate/scripts/epf-validate.ps1 -ObjectPath "<путь_к_обработке>" ``` ## Проверки diff --git a/.claude/skills/form-validate/SKILL.md b/.claude/skills/form-validate/SKILL.md index 9a07c493..304f23b6 100644 --- a/.claude/skills/form-validate/SKILL.md +++ b/.claude/skills/form-validate/SKILL.md @@ -30,9 +30,11 @@ allowed-tools: ## Команда ```powershell -powershell.exe -NoProfile -File .claude/skills/form-validate/scripts/form-validate.ps1 -FormPath "<путь>" +powershell.exe -NoProfile -File .claude/skills/form-validate/scripts/form-validate.ps1 -FormPath "<.../Forms/ИмяФормы>" ``` +Можно указать директорию формы — скрипт найдёт Ext/Form.xml автоматически. + ## Проверки | # | Проверка | Серьёзность | diff --git a/.claude/skills/form-validate/scripts/form-validate.ps1 b/.claude/skills/form-validate/scripts/form-validate.ps1 index 900ee59a..6f572b98 100644 --- a/.claude/skills/form-validate/scripts/form-validate.ps1 +++ b/.claude/skills/form-validate/scripts/form-validate.ps1 @@ -12,6 +12,27 @@ param( $ErrorActionPreference = "Stop" [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 +# --- Resolve path --- +# A: Directory → Ext/Form.xml +if (Test-Path $FormPath -PathType Container) { + $FormPath = Join-Path (Join-Path $FormPath "Ext") "Form.xml" +} +# B1: Missing Ext/ (e.g. Forms/Форма/Form.xml → Forms/Форма/Ext/Form.xml) +if (-not (Test-Path $FormPath)) { + $fn = [System.IO.Path]::GetFileName($FormPath) + if ($fn -eq "Form.xml") { + $c = Join-Path (Join-Path (Split-Path $FormPath) "Ext") $fn + if (Test-Path $c) { $FormPath = $c } + } +} +# B2: Descriptor (Forms/Форма.xml → Forms/Форма/Ext/Form.xml) +if (-not (Test-Path $FormPath) -and $FormPath.EndsWith(".xml")) { + $stem = [System.IO.Path]::GetFileNameWithoutExtension($FormPath) + $dir = Split-Path $FormPath + $c = Join-Path (Join-Path (Join-Path $dir $stem) "Ext") "Form.xml" + if (Test-Path $c) { $FormPath = $c } +} + # --- Load XML --- if (-not (Test-Path $FormPath)) { diff --git a/.claude/skills/form-validate/scripts/form-validate.py b/.claude/skills/form-validate/scripts/form-validate.py index 13042016..ff44ed1e 100644 --- a/.claude/skills/form-validate/scripts/form-validate.py +++ b/.claude/skills/form-validate/scripts/form-validate.py @@ -31,6 +31,27 @@ def main(): detailed = args.Detailed max_errors = args.MaxErrors + if not os.path.isabs(form_path): + form_path = os.path.join(os.getcwd(), form_path) + + # A: Directory → Ext/Form.xml + if os.path.isdir(form_path): + form_path = os.path.join(form_path, 'Ext', 'Form.xml') + # B1: Missing Ext/ (e.g. Forms/Форма/Form.xml → Forms/Форма/Ext/Form.xml) + if not os.path.exists(form_path): + fn = os.path.basename(form_path) + if fn == 'Form.xml': + c = os.path.join(os.path.dirname(form_path), 'Ext', fn) + if os.path.exists(c): + form_path = c + # B2: Descriptor (Forms/Форма.xml → Forms/Форма/Ext/Form.xml) + if not os.path.exists(form_path) and form_path.endswith('.xml'): + stem = os.path.splitext(os.path.basename(form_path))[0] + parent = os.path.dirname(form_path) + c = os.path.join(parent, stem, 'Ext', 'Form.xml') + if os.path.exists(c): + form_path = c + if not os.path.isfile(form_path): print(f"File not found: {form_path}", file=sys.stderr) sys.exit(1) diff --git a/.claude/skills/interface-validate/SKILL.md b/.claude/skills/interface-validate/SKILL.md index ae0eeb3e..91f645d7 100644 --- a/.claude/skills/interface-validate/SKILL.md +++ b/.claude/skills/interface-validate/SKILL.md @@ -31,9 +31,11 @@ allowed-tools: ## Команда ```powershell -powershell.exe -NoProfile -File '.claude/skills/interface-validate/scripts/interface-validate.ps1' -CIPath '' +powershell.exe -NoProfile -File ".claude/skills/interface-validate/scripts/interface-validate.ps1" -CIPath "" ``` +Можно указать директорию подсистемы — скрипт найдёт Ext/CommandInterface.xml автоматически. + ## Проверки (13) | # | Проверка | Серьёзность | diff --git a/.claude/skills/interface-validate/scripts/interface-validate.ps1 b/.claude/skills/interface-validate/scripts/interface-validate.ps1 index f153cfa9..0bd5191a 100644 --- a/.claude/skills/interface-validate/scripts/interface-validate.ps1 +++ b/.claude/skills/interface-validate/scripts/interface-validate.ps1 @@ -14,6 +14,18 @@ $ErrorActionPreference = "Stop" if (-not [System.IO.Path]::IsPathRooted($CIPath)) { $CIPath = Join-Path (Get-Location).Path $CIPath } +# A: Directory → Ext/CommandInterface.xml +if (Test-Path $CIPath -PathType Container) { + $CIPath = Join-Path (Join-Path $CIPath "Ext") "CommandInterface.xml" +} +# B1: Missing Ext/ (e.g. Subsystems/X/CommandInterface.xml → Subsystems/X/Ext/CommandInterface.xml) +if (-not (Test-Path $CIPath)) { + $fn = [System.IO.Path]::GetFileName($CIPath) + if ($fn -eq "CommandInterface.xml") { + $c = Join-Path (Join-Path (Split-Path $CIPath) "Ext") $fn + if (Test-Path $c) { $CIPath = $c } + } +} if (-not (Test-Path $CIPath)) { Write-Host "[ERROR] File not found: $CIPath" exit 1 diff --git a/.claude/skills/interface-validate/scripts/interface-validate.py b/.claude/skills/interface-validate/scripts/interface-validate.py index ee81894d..4eeb181b 100644 --- a/.claude/skills/interface-validate/scripts/interface-validate.py +++ b/.claude/skills/interface-validate/scripts/interface-validate.py @@ -94,6 +94,17 @@ def main(): if not os.path.isabs(ci_path): ci_path = os.path.join(os.getcwd(), ci_path) + # A: Directory → Ext/CommandInterface.xml + if os.path.isdir(ci_path): + ci_path = os.path.join(ci_path, 'Ext', 'CommandInterface.xml') + # B1: Missing Ext/ + if not os.path.exists(ci_path): + fn = os.path.basename(ci_path) + if fn == 'CommandInterface.xml': + c = os.path.join(os.path.dirname(ci_path), 'Ext', fn) + if os.path.exists(c): + ci_path = c + if not os.path.exists(ci_path): print(f'[ERROR] File not found: {ci_path}') sys.exit(1) diff --git a/.claude/skills/meta-validate/SKILL.md b/.claude/skills/meta-validate/SKILL.md index 627ab1e3..abcd59d5 100644 --- a/.claude/skills/meta-validate/SKILL.md +++ b/.claude/skills/meta-validate/SKILL.md @@ -33,7 +33,7 @@ allowed-tools: ## Команда ```powershell -powershell.exe -NoProfile -File .claude/skills/meta-validate/scripts/meta-validate.ps1 -ObjectPath "<путь>" +powershell.exe -NoProfile -File .claude/skills/meta-validate/scripts/meta-validate.ps1 -ObjectPath "" ``` ## Поддерживаемые типы (23) diff --git a/.claude/skills/mxl-validate/SKILL.md b/.claude/skills/mxl-validate/SKILL.md index e3b6a639..5c2e19f9 100644 --- a/.claude/skills/mxl-validate/SKILL.md +++ b/.claude/skills/mxl-validate/SKILL.md @@ -35,9 +35,11 @@ allowed-tools: ## Команда ```powershell -powershell.exe -NoProfile -File .claude/skills/mxl-validate/scripts/mxl-validate.ps1 -TemplatePath "<путь>" +powershell.exe -NoProfile -File .claude/skills/mxl-validate/scripts/mxl-validate.ps1 -TemplatePath "<.../Templates/ИмяМакета>" ``` +Можно указать директорию макета — скрипт найдёт Ext/Template.xml автоматически. + ## Проверки | # | Проверка | Серьёзность | diff --git a/.claude/skills/mxl-validate/scripts/mxl-validate.ps1 b/.claude/skills/mxl-validate/scripts/mxl-validate.ps1 index ba35f8fb..0657100e 100644 --- a/.claude/skills/mxl-validate/scripts/mxl-validate.ps1 +++ b/.claude/skills/mxl-validate/scripts/mxl-validate.ps1 @@ -22,6 +22,26 @@ if (-not $TemplatePath) { $TemplatePath = Join-Path (Join-Path (Join-Path (Join-Path (Join-Path $SrcDir $ProcessorName) "Templates") $TemplateName) "Ext") "Template.xml" } +# A: Directory → Ext/Template.xml +if (Test-Path $TemplatePath -PathType Container) { + $TemplatePath = Join-Path (Join-Path $TemplatePath "Ext") "Template.xml" +} +# B1: Missing Ext/ (e.g. Templates/Макет/Template.xml → Templates/Макет/Ext/Template.xml) +if (-not (Test-Path $TemplatePath)) { + $fn = [System.IO.Path]::GetFileName($TemplatePath) + if ($fn -eq "Template.xml") { + $c = Join-Path (Join-Path (Split-Path $TemplatePath) "Ext") $fn + if (Test-Path $c) { $TemplatePath = $c } + } +} +# B2: Descriptor (Templates/Макет.xml → Templates/Макет/Ext/Template.xml) +if (-not (Test-Path $TemplatePath) -and $TemplatePath.EndsWith(".xml")) { + $stem = [System.IO.Path]::GetFileNameWithoutExtension($TemplatePath) + $dir = Split-Path $TemplatePath + $c = Join-Path (Join-Path (Join-Path $dir $stem) "Ext") "Template.xml" + if (Test-Path $c) { $TemplatePath = $c } +} + if (-not (Test-Path $TemplatePath)) { Write-Error "File not found: $TemplatePath" exit 1 diff --git a/.claude/skills/mxl-validate/scripts/mxl-validate.py b/.claude/skills/mxl-validate/scripts/mxl-validate.py index 3cecfbbc..fcbef905 100644 --- a/.claude/skills/mxl-validate/scripts/mxl-validate.py +++ b/.claude/skills/mxl-validate/scripts/mxl-validate.py @@ -81,6 +81,24 @@ def main(): if not os.path.isabs(template_path): template_path = os.path.join(os.getcwd(), template_path) + # A: Directory → Ext/Template.xml + if os.path.isdir(template_path): + template_path = os.path.join(template_path, 'Ext', 'Template.xml') + # B1: Missing Ext/ (e.g. Templates/Макет/Template.xml → Templates/Макет/Ext/Template.xml) + if not os.path.exists(template_path): + fn = os.path.basename(template_path) + if fn == 'Template.xml': + c = os.path.join(os.path.dirname(template_path), 'Ext', fn) + if os.path.exists(c): + template_path = c + # B2: Descriptor (Templates/Макет.xml → Templates/Макет/Ext/Template.xml) + if not os.path.exists(template_path) and template_path.endswith('.xml'): + stem = os.path.splitext(os.path.basename(template_path))[0] + parent = os.path.dirname(template_path) + c = os.path.join(parent, stem, 'Ext', 'Template.xml') + if os.path.exists(c): + template_path = c + if not os.path.exists(template_path): print(f'File not found: {template_path}', file=sys.stderr) sys.exit(1) diff --git a/.claude/skills/role-validate/SKILL.md b/.claude/skills/role-validate/SKILL.md index 9f604df6..f0bd611d 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: [-Detailed] [-MetadataPath ] +argument-hint: [-Detailed] [-MaxErrors 30] [-MetadataPath ] allowed-tools: - Bash - Read @@ -25,6 +25,7 @@ allowed-tools: | RightsPath | да | — | Путь к `Rights.xml` роли | | MetadataPath | нет | — | Путь к метаданным роли (`Roles/ИмяРоли.xml`) | | Detailed | нет | — | Показывать [OK] для каждой проверки | +| MaxErrors | нет | 30 | Макс. ошибок до остановки (по умолчанию 30) | | OutFile | нет | — | Записать результат в файл (UTF-8 BOM) | **Важно:** Для кириллических путей используй `-OutFile` и читай результат через Read tool. @@ -32,9 +33,11 @@ allowed-tools: ## Команда ```powershell -powershell.exe -NoProfile -File .claude/skills/role-validate/scripts/role-validate.ps1 -RightsPath [-MetadataPath ] +powershell.exe -NoProfile -File .claude/skills/role-validate/scripts/role-validate.ps1 -RightsPath "" [-MetadataPath ""] ``` +Можно указать директорию роли — скрипт найдёт Ext/Rights.xml автоматически. + ## Проверки | # | Проверка | Серьёзность | diff --git a/.claude/skills/role-validate/scripts/role-validate.ps1 b/.claude/skills/role-validate/scripts/role-validate.ps1 index f3ba2cdd..ac26567e 100644 --- a/.claude/skills/role-validate/scripts/role-validate.ps1 +++ b/.claude/skills/role-validate/scripts/role-validate.ps1 @@ -189,6 +189,23 @@ function Find-Similar { return $result } +# --- Resolve path --- +if (-not [System.IO.Path]::IsPathRooted($RightsPath)) { + $RightsPath = Join-Path (Get-Location).Path $RightsPath +} +# A: Directory → Ext/Rights.xml +if (Test-Path $RightsPath -PathType Container) { + $RightsPath = Join-Path (Join-Path $RightsPath "Ext") "Rights.xml" +} +# B1: Missing Ext/ (e.g. Roles/МояРоль/Rights.xml → Roles/МояРоль/Ext/Rights.xml) +if (-not (Test-Path $RightsPath)) { + $fn = [System.IO.Path]::GetFileName($RightsPath) + if ($fn -eq "Rights.xml") { + $c = Join-Path (Join-Path (Split-Path $RightsPath) "Ext") $fn + if (Test-Path $c) { $RightsPath = $c } + } +} + # --- 3. Validate Rights.xml --- if (-not (Test-Path $RightsPath)) { diff --git a/.claude/skills/role-validate/scripts/role-validate.py b/.claude/skills/role-validate/scripts/role-validate.py index 688b1652..54c9a587 100644 --- a/.claude/skills/role-validate/scripts/role-validate.py +++ b/.claude/skills/role-validate/scripts/role-validate.py @@ -193,6 +193,17 @@ def main(): if not os.path.isabs(rights_path): rights_path = os.path.join(os.getcwd(), rights_path) + # A: Directory → Ext/Rights.xml + if os.path.isdir(rights_path): + rights_path = os.path.join(rights_path, 'Ext', 'Rights.xml') + # B1: Missing Ext/ + if not os.path.exists(rights_path): + fn = os.path.basename(rights_path) + if fn == 'Rights.xml': + c = os.path.join(os.path.dirname(rights_path), 'Ext', fn) + if os.path.exists(c): + rights_path = c + # --- Output helpers --- lines = [] errors = 0 diff --git a/.claude/skills/skd-validate/SKILL.md b/.claude/skills/skd-validate/SKILL.md index 6be77895..14ee3d4b 100644 --- a/.claude/skills/skd-validate/SKILL.md +++ b/.claude/skills/skd-validate/SKILL.md @@ -33,9 +33,11 @@ allowed-tools: ## Команда ```powershell -powershell.exe -NoProfile -File .claude/skills/skd-validate/scripts/skd-validate.ps1 -TemplatePath "<путь>" +powershell.exe -NoProfile -File .claude/skills/skd-validate/scripts/skd-validate.ps1 -TemplatePath "<.../Templates/ИмяМакета>" ``` +Можно указать директорию макета — скрипт найдёт Ext/Template.xml автоматически. + ## Проверки (~30) | Группа | Что проверяется | diff --git a/.claude/skills/skd-validate/scripts/skd-validate.ps1 b/.claude/skills/skd-validate/scripts/skd-validate.ps1 index 7a91890f..99e4bfa2 100644 --- a/.claude/skills/skd-validate/scripts/skd-validate.ps1 +++ b/.claude/skills/skd-validate/scripts/skd-validate.ps1 @@ -16,12 +16,28 @@ $ErrorActionPreference = "Stop" # --- Resolve path --- -if (-not $TemplatePath.EndsWith(".xml")) { - $candidate = Join-Path (Join-Path $TemplatePath "Ext") "Template.xml" - if (Test-Path $candidate) { - $TemplatePath = $candidate +if (-not [System.IO.Path]::IsPathRooted($TemplatePath)) { + $TemplatePath = Join-Path (Get-Location).Path $TemplatePath +} +# A: Directory → Ext/Template.xml +if (Test-Path $TemplatePath -PathType Container) { + $TemplatePath = Join-Path (Join-Path $TemplatePath "Ext") "Template.xml" +} +# B1: Missing Ext/ (e.g. Templates/СКД/Template.xml → Templates/СКД/Ext/Template.xml) +if (-not (Test-Path $TemplatePath)) { + $fn = [System.IO.Path]::GetFileName($TemplatePath) + if ($fn -eq "Template.xml") { + $c = Join-Path (Join-Path (Split-Path $TemplatePath) "Ext") $fn + if (Test-Path $c) { $TemplatePath = $c } } } +# B2: Descriptor (Templates/СКД.xml → Templates/СКД/Ext/Template.xml) +if (-not (Test-Path $TemplatePath) -and $TemplatePath.EndsWith(".xml")) { + $stem = [System.IO.Path]::GetFileNameWithoutExtension($TemplatePath) + $dir = Split-Path $TemplatePath + $c = Join-Path (Join-Path (Join-Path $dir $stem) "Ext") "Template.xml" + if (Test-Path $c) { $TemplatePath = $c } +} if (-not (Test-Path $TemplatePath)) { Write-Error "File not found: $TemplatePath" diff --git a/.claude/skills/skd-validate/scripts/skd-validate.py b/.claude/skills/skd-validate/scripts/skd-validate.py index 3b76d3f3..8c7b38d4 100644 --- a/.claude/skills/skd-validate/scripts/skd-validate.py +++ b/.claude/skills/skd-validate/scripts/skd-validate.py @@ -25,10 +25,23 @@ out_file = args.OutFile # ── resolve path ───────────────────────────────────────────── -if not template_path.endswith(".xml"): - candidate = os.path.join(template_path, "Ext", "Template.xml") - if os.path.exists(candidate): - template_path = candidate +# A: Directory → Ext/Template.xml +if os.path.isdir(template_path): + template_path = os.path.join(template_path, 'Ext', 'Template.xml') +# B1: Missing Ext/ (e.g. Templates/СКД/Template.xml → Templates/СКД/Ext/Template.xml) +if not os.path.exists(template_path): + fn = os.path.basename(template_path) + if fn == 'Template.xml': + c = os.path.join(os.path.dirname(template_path), 'Ext', fn) + if os.path.exists(c): + template_path = c +# B2: Descriptor (.xml → dir/Ext/Template.xml) +if not os.path.exists(template_path) and template_path.endswith('.xml'): + stem = os.path.splitext(os.path.basename(template_path))[0] + parent = os.path.dirname(template_path) + c = os.path.join(parent, stem, 'Ext', 'Template.xml') + if os.path.exists(c): + template_path = c if not os.path.exists(template_path): print(f"File not found: {template_path}", file=sys.stderr) diff --git a/.claude/skills/subsystem-validate/SKILL.md b/.claude/skills/subsystem-validate/SKILL.md index 75673663..9d4314ca 100644 --- a/.claude/skills/subsystem-validate/SKILL.md +++ b/.claude/skills/subsystem-validate/SKILL.md @@ -31,9 +31,11 @@ allowed-tools: ## Команда ```powershell -powershell.exe -NoProfile -File '.claude/skills/subsystem-validate/scripts/subsystem-validate.ps1' -SubsystemPath '<путь>' +powershell.exe -NoProfile -File ".claude/skills/subsystem-validate/scripts/subsystem-validate.ps1" -SubsystemPath "" ``` +Можно указать директорию подсистемы — скрипт найдёт XML-файл автоматически. + ## Проверки (13) | # | Проверка | Серьёзность | From ffdee04a9570074dd1123b40d541adf36c764998 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Mon, 9 Mar 2026 19:07:16 +0300 Subject: [PATCH 3/7] refactor(validate): auto-detect metadata in role-validate, clean up SKILL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit role-validate: remove MetadataPath param, auto-detect from RightsPath (Roles/Name/Ext/Rights.xml → Roles/Name.xml). Always validate metadata when file exists (was 7 checks, now 10). Deduplicate path computation. SKILL.md: remove redundant auto-resolve notes (placeholder already shows directory path), fix role-validate examples, replace mxl-validate ProcessorName/TemplateName with concrete path examples. Co-Authored-By: Claude Opus 4.6 --- .claude/skills/form-validate/SKILL.md | 2 - .claude/skills/interface-validate/SKILL.md | 2 - .claude/skills/mxl-validate/SKILL.md | 13 +- .claude/skills/role-validate/SKILL.md | 12 +- .../role-validate/scripts/role-validate.ps1 | 87 +++++++------ .../role-validate/scripts/role-validate.py | 119 ++++++++---------- .claude/skills/skd-validate/SKILL.md | 4 - .claude/skills/subsystem-validate/SKILL.md | 2 - 8 files changed, 99 insertions(+), 142 deletions(-) diff --git a/.claude/skills/form-validate/SKILL.md b/.claude/skills/form-validate/SKILL.md index 304f23b6..c344b573 100644 --- a/.claude/skills/form-validate/SKILL.md +++ b/.claude/skills/form-validate/SKILL.md @@ -33,8 +33,6 @@ allowed-tools: powershell.exe -NoProfile -File .claude/skills/form-validate/scripts/form-validate.ps1 -FormPath "<.../Forms/ИмяФормы>" ``` -Можно указать директорию формы — скрипт найдёт Ext/Form.xml автоматически. - ## Проверки | # | Проверка | Серьёзность | diff --git a/.claude/skills/interface-validate/SKILL.md b/.claude/skills/interface-validate/SKILL.md index 91f645d7..b3bf8de3 100644 --- a/.claude/skills/interface-validate/SKILL.md +++ b/.claude/skills/interface-validate/SKILL.md @@ -34,8 +34,6 @@ allowed-tools: powershell.exe -NoProfile -File ".claude/skills/interface-validate/scripts/interface-validate.ps1" -CIPath "" ``` -Можно указать директорию подсистемы — скрипт найдёт Ext/CommandInterface.xml автоматически. - ## Проверки (13) | # | Проверка | Серьёзность | diff --git a/.claude/skills/mxl-validate/SKILL.md b/.claude/skills/mxl-validate/SKILL.md index 5c2e19f9..4bdcb60c 100644 --- a/.claude/skills/mxl-validate/SKILL.md +++ b/.claude/skills/mxl-validate/SKILL.md @@ -15,31 +15,24 @@ allowed-tools: ## Использование ``` -/mxl-validate -/mxl-validate -ProcessorName "МояОбработка" -TemplateName "Макет" +/mxl-validate Catalogs/Номенклатура/Templates/Макет +/mxl-validate src/МояОбработка/Templates/ПечатнаяФорма ``` ## Параметры | Параметр | Обяз. | Умолч. | Описание | |---------------|:-----:|---------|--------------------------------------------| -| TemplatePath | нет | — | Прямой путь к Template.xml | -| ProcessorName | нет | — | Имя обработки (альтернатива пути) | -| TemplateName | нет | — | Имя макета (альтернатива пути) | -| SrcDir | нет | `src` | Каталог исходников | +| TemplatePath | да | — | Путь к макету (директория или Template.xml) | | Detailed | нет | — | Показывать [OK] для каждой проверки | | MaxErrors | нет | 20 | Остановиться после N ошибок | -Укажите либо `-TemplatePath`, либо оба `-ProcessorName` и `-TemplateName`. - ## Команда ```powershell powershell.exe -NoProfile -File .claude/skills/mxl-validate/scripts/mxl-validate.ps1 -TemplatePath "<.../Templates/ИмяМакета>" ``` -Можно указать директорию макета — скрипт найдёт Ext/Template.xml автоматически. - ## Проверки | # | Проверка | Серьёзность | diff --git a/.claude/skills/role-validate/SKILL.md b/.claude/skills/role-validate/SKILL.md index f0bd611d..6583bc96 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: [-Detailed] [-MaxErrors 30] [-MetadataPath ] +argument-hint: [-Detailed] [-MaxErrors 30] allowed-tools: - Bash - Read @@ -14,16 +14,14 @@ allowed-tools: ## Использование ``` -/role-validate -/role-validate Roles/МояРоль/Ext/Rights.xml Roles/МояРоль.xml +/role-validate Roles/МояРоль ``` ## Параметры | Параметр | Обяз. | Умолч. | Описание | |--------------|:-----:|---------|-------------------------------------------------| -| RightsPath | да | — | Путь к `Rights.xml` роли | -| MetadataPath | нет | — | Путь к метаданным роли (`Roles/ИмяРоли.xml`) | +| RightsPath | да | — | Путь к роли (директория или `Rights.xml`) | | Detailed | нет | — | Показывать [OK] для каждой проверки | | MaxErrors | нет | 30 | Макс. ошибок до остановки (по умолчанию 30) | | OutFile | нет | — | Записать результат в файл (UTF-8 BOM) | @@ -33,11 +31,9 @@ allowed-tools: ## Команда ```powershell -powershell.exe -NoProfile -File .claude/skills/role-validate/scripts/role-validate.ps1 -RightsPath "" [-MetadataPath ""] +powershell.exe -NoProfile -File .claude/skills/role-validate/scripts/role-validate.ps1 -RightsPath "" ``` -Можно указать директорию роли — скрипт найдёт Ext/Rights.xml автоматически. - ## Проверки | # | Проверка | Серьёзность | diff --git a/.claude/skills/role-validate/scripts/role-validate.ps1 b/.claude/skills/role-validate/scripts/role-validate.ps1 index ac26567e..15de5400 100644 --- a/.claude/skills/role-validate/scripts/role-validate.ps1 +++ b/.claude/skills/role-validate/scripts/role-validate.ps1 @@ -4,8 +4,6 @@ param( [Parameter(Mandatory)] [string]$RightsPath, - [string]$MetadataPath, - [string]$OutFile, [switch]$Detailed, @@ -223,6 +221,14 @@ if (-not (Test-Path $RightsPath)) { exit 1 } +# Auto-detect metadata: Roles/Name/Ext/Rights.xml → Roles/Name.xml +$resolvedRights = (Resolve-Path $RightsPath).Path +$extDir = Split-Path $resolvedRights -Parent +$roleDir = Split-Path $extDir -Parent +$rolesDir = Split-Path $roleDir -Parent +$roleDirName = Split-Path $roleDir -Leaf +$MetadataPath = Join-Path $rolesDir "$roleDirName.xml" + # 3a. Parse XML try { [xml]$xml = Get-Content -Path $RightsPath -Encoding UTF8 @@ -394,59 +400,50 @@ if ($templates.Count -gt 0) { Report-OK "$($templates.Count) templates: $($tplNames -join ', ')" } -# --- 4. Validate metadata (optional) --- +# --- 4. Validate metadata --- -if ($MetadataPath) { +if (Test-Path $MetadataPath) { Out-Line "" - - if (-not (Test-Path $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) { - Report-Error "Metadata: element not found" + try { + [xml]$metaXml = Get-Content -Path $MetadataPath -Encoding UTF8 + $roleNode = $metaXml.DocumentElement.SelectSingleNode("//*[local-name()='Role']") + if (-not $roleNode) { + 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}$') { + Report-OK "Metadata: UUID valid ($uuid)" } 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}$') { - Report-OK "Metadata: UUID valid ($uuid)" - } else { - Report-Error "Metadata: invalid UUID format '$uuid'" - } - - $nameNode = $roleNode.SelectSingleNode(".//*[local-name()='Name']") - if ($nameNode -and $nameNode.InnerText) { - Report-OK "Metadata: Name = $($nameNode.InnerText)" - } else { - Report-Error "Metadata: is empty or missing" - } - - $synNode = $roleNode.SelectSingleNode(".//*[local-name()='Synonym']") - if ($synNode -and $synNode.InnerXml) { - Report-OK "Metadata: Synonym present" - } else { - Report-Warn "Metadata: is empty" - } + Report-Error "Metadata: invalid UUID format '$uuid'" + } + + $nameNode = $roleNode.SelectSingleNode(".//*[local-name()='Name']") + if ($nameNode -and $nameNode.InnerText) { + Report-OK "Metadata: Name = $($nameNode.InnerText)" + } else { + Report-Error "Metadata: is empty or missing" + } + + $synNode = $roleNode.SelectSingleNode(".//*[local-name()='Synonym']") + if ($synNode -and $synNode.InnerXml) { + Report-OK "Metadata: Synonym present" + } else { + Report-Warn "Metadata: is empty" } - } catch { - Report-Error "Metadata XML parse error: $($_.Exception.Message)" } + } catch { + Report-Error "Metadata XML parse error: $($_.Exception.Message)" } } # --- 5. Check registration in Configuration.xml --- -# Infer paths: RightsPath = .../Roles/Name/Ext/Rights.xml -$extDir2 = Split-Path (Resolve-Path $RightsPath).Path -Parent -$roleDir2 = Split-Path $extDir2 -Parent -$rolesDir2 = Split-Path $roleDir2 -Parent -$configDir2 = Split-Path $rolesDir2 -Parent -$configXmlPath2 = Join-Path $configDir2 "Configuration.xml" -$inferredRoleName = Split-Path $roleDir2 -Leaf +$configDir = Split-Path $rolesDir -Parent +$configXmlPath = Join-Path $configDir "Configuration.xml" +$inferredRoleName = $roleDirName # Use metadata name if available -if ($MetadataPath -and (Test-Path $MetadataPath)) { +if (Test-Path $MetadataPath) { try { [xml]$metaXml2 = Get-Content -Path $MetadataPath -Encoding UTF8 $nameNode2 = $metaXml2.DocumentElement.SelectSingleNode("//*[local-name()='Role']//*[local-name()='Name']") @@ -456,10 +453,10 @@ if ($MetadataPath -and (Test-Path $MetadataPath)) { } catch { } } -if (Test-Path $configXmlPath2) { +if (Test-Path $configXmlPath) { Out-Line "" try { - [xml]$cfgXml = Get-Content -Path $configXmlPath2 -Encoding UTF8 + [xml]$cfgXml = Get-Content -Path $configXmlPath -Encoding UTF8 $cfgNs = New-Object System.Xml.XmlNamespaceManager($cfgXml.NameTable) $cfgNs.AddNamespace("md", "http://v8.1c.ru/8.3/MDClasses") $childObj = $cfgXml.SelectSingleNode("//md:Configuration/md:ChildObjects", $cfgNs) diff --git a/.claude/skills/role-validate/scripts/role-validate.py b/.claude/skills/role-validate/scripts/role-validate.py index 54c9a587..7ff6d86a 100644 --- a/.claude/skills/role-validate/scripts/role-validate.py +++ b/.claude/skills/role-validate/scripts/role-validate.py @@ -180,14 +180,12 @@ def main(): description='Validate 1C role Rights.xml structure', allow_abbrev=False ) 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 - metadata_path = args.MetadataPath out_file = args.OutFile if not os.path.isabs(rights_path): @@ -204,6 +202,15 @@ def main(): if os.path.exists(c): rights_path = c + resolved_path = os.path.abspath(rights_path) + + # Auto-detect metadata: Roles/Name/Ext/Rights.xml → Roles/Name.xml + ext_dir = os.path.dirname(resolved_path) + role_dir = os.path.dirname(ext_dir) + roles_dir = os.path.dirname(role_dir) + role_dir_name = os.path.basename(role_dir) + metadata_path = os.path.join(roles_dir, f'{role_dir_name}.xml') + # --- Output helpers --- lines = [] errors = 0 @@ -407,89 +414,63 @@ def main(): # --- 4. Validate metadata (optional) --- inferred_role_name = '' - if metadata_path: + if os.path.isfile(metadata_path): lines.append('') - if not os.path.isabs(metadata_path): - metadata_path = os.path.join(os.getcwd(), metadata_path) + try: + meta_parser = etree.XMLParser(remove_blank_text=False) + meta_xml = etree.parse(metadata_path, meta_parser) + meta_root = meta_xml.getroot() + # Find element anywhere + role_node = None + for el in meta_root.iter(): + if isinstance(el.tag, str) and etree.QName(el.tag).localname == 'Role': + role_node = el + break - if not os.path.exists(metadata_path): - report_error(f'Metadata file not found: {metadata_path}') - else: - try: - meta_parser = etree.XMLParser(remove_blank_text=False) - meta_xml = etree.parse(metadata_path, meta_parser) - meta_root = meta_xml.getroot() - # Find element anywhere - role_node = None - for el in meta_root.iter(): - if isinstance(el.tag, str) and etree.QName(el.tag).localname == 'Role': - role_node = el + if role_node is None: + report_error('Metadata: element not found') + else: + uuid_val = role_node.get('uuid', '') + if GUID_PATTERN.match(uuid_val): + report_ok(f'Metadata: UUID valid ({uuid_val})') + else: + report_error(f"Metadata: invalid UUID format '{uuid_val}'") + + # Find Name + name_node = None + for el in role_node.iter(): + if isinstance(el.tag, str) and etree.QName(el.tag).localname == 'Name': + name_node = el break - if role_node is None: - report_error('Metadata: element not found') + if name_node is not None and name_node.text: + report_ok(f'Metadata: Name = {name_node.text}') + inferred_role_name = name_node.text else: - uuid_val = role_node.get('uuid', '') - if GUID_PATTERN.match(uuid_val): - report_ok(f'Metadata: UUID valid ({uuid_val})') - else: - report_error(f"Metadata: invalid UUID format '{uuid_val}'") + report_error('Metadata: is empty or missing') - # Find Name - name_node = None - for el in role_node.iter(): - if isinstance(el.tag, str) and etree.QName(el.tag).localname == 'Name': - name_node = el - break + # Find Synonym + syn_node = None + for el in role_node.iter(): + if isinstance(el.tag, str) and etree.QName(el.tag).localname == 'Synonym': + syn_node = el + break - if name_node is not None and name_node.text: - report_ok(f'Metadata: Name = {name_node.text}') - inferred_role_name = name_node.text - else: - report_error('Metadata: is empty or missing') - - # Find Synonym - syn_node = None - for el in role_node.iter(): - if isinstance(el.tag, str) and etree.QName(el.tag).localname == 'Synonym': - syn_node = el - break - - if syn_node is not None and len(syn_node) > 0: - report_ok('Metadata: Synonym present') - else: - report_warn('Metadata: is empty') - except etree.XMLSyntaxError as e: - report_error(f'Metadata XML parse error: {e}') + if syn_node is not None and len(syn_node) > 0: + report_ok('Metadata: Synonym present') + else: + report_warn('Metadata: is empty') + except etree.XMLSyntaxError as e: + report_error(f'Metadata XML parse error: {e}') # --- 5. Check registration in Configuration.xml --- - resolved_rights = os.path.abspath(rights_path) - ext_dir = os.path.dirname(resolved_rights) # Ext - role_dir = os.path.dirname(ext_dir) # RoleName - roles_dir = os.path.dirname(role_dir) # Roles config_dir = os.path.dirname(roles_dir) # config root config_xml_path = os.path.join(config_dir, 'Configuration.xml') if not inferred_role_name: inferred_role_name = os.path.basename(role_dir) - # Use metadata name if available (already set above if metadata was parsed) - if metadata_path and os.path.exists(metadata_path) and not inferred_role_name: - try: - meta_parser2 = etree.XMLParser(remove_blank_text=False) - meta_xml2 = etree.parse(metadata_path, meta_parser2) - for el in meta_xml2.getroot().iter(): - if isinstance(el.tag, str) and etree.QName(el.tag).localname == 'Role': - for el2 in el.iter(): - if isinstance(el2.tag, str) and etree.QName(el2.tag).localname == 'Name': - if el2.text: - inferred_role_name = el2.text - break - break - except Exception: - pass - if os.path.exists(config_xml_path): lines.append('') try: diff --git a/.claude/skills/skd-validate/SKILL.md b/.claude/skills/skd-validate/SKILL.md index 14ee3d4b..f5ccc43d 100644 --- a/.claude/skills/skd-validate/SKILL.md +++ b/.claude/skills/skd-validate/SKILL.md @@ -19,8 +19,6 @@ allowed-tools: /skd-validate path/to/Ext/Template.xml ``` -`TemplatePath` авторезолв: если указан каталог макета — ищет `Ext/Template.xml`. - ## Параметры | Параметр | Обяз. | Умолч. | Описание | @@ -36,8 +34,6 @@ allowed-tools: powershell.exe -NoProfile -File .claude/skills/skd-validate/scripts/skd-validate.ps1 -TemplatePath "<.../Templates/ИмяМакета>" ``` -Можно указать директорию макета — скрипт найдёт Ext/Template.xml автоматически. - ## Проверки (~30) | Группа | Что проверяется | diff --git a/.claude/skills/subsystem-validate/SKILL.md b/.claude/skills/subsystem-validate/SKILL.md index 9d4314ca..685ab238 100644 --- a/.claude/skills/subsystem-validate/SKILL.md +++ b/.claude/skills/subsystem-validate/SKILL.md @@ -34,8 +34,6 @@ allowed-tools: powershell.exe -NoProfile -File ".claude/skills/subsystem-validate/scripts/subsystem-validate.ps1" -SubsystemPath "" ``` -Можно указать директорию подсистемы — скрипт найдёт XML-файл автоматически. - ## Проверки (13) | # | Проверка | Серьёзность | From 29b124f3fdd024f7d017041bb1003e3b2ec3ef85 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Mon, 9 Mar 2026 19:17:12 +0300 Subject: [PATCH 4/7] docs(validate): concrete examples in SKILL.md, remove auto-resolve notes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace abstract <...> placeholders with concrete paths in ## Команда - Replace abstract examples with concrete paths in ## Использование - Remove авторезолв notes from meta/epf/cf/cfe-validate - Clean up erf-validate: remove Вывод/Верификация/Когда использовать, add -Detailed parameter, unify format with other validators Co-Authored-By: Claude Opus 4.6 --- .claude/skills/cf-validate/SKILL.md | 8 ++- .claude/skills/cfe-validate/SKILL.md | 8 ++- .claude/skills/epf-validate/SKILL.md | 8 ++- .claude/skills/erf-validate/SKILL.md | 58 +++++----------------- .claude/skills/form-validate/SKILL.md | 4 +- .claude/skills/interface-validate/SKILL.md | 4 +- .claude/skills/meta-validate/SKILL.md | 8 ++- .claude/skills/mxl-validate/SKILL.md | 2 +- .claude/skills/role-validate/SKILL.md | 2 +- .claude/skills/skd-validate/SKILL.md | 6 +-- .claude/skills/subsystem-validate/SKILL.md | 4 +- 11 files changed, 35 insertions(+), 77 deletions(-) diff --git a/.claude/skills/cf-validate/SKILL.md b/.claude/skills/cf-validate/SKILL.md index 8d67bd09..e46399dc 100644 --- a/.claude/skills/cf-validate/SKILL.md +++ b/.claude/skills/cf-validate/SKILL.md @@ -15,12 +15,10 @@ allowed-tools: ## Использование ``` -/cf-validate -/cf-validate upload/cfempty — каталог выгрузки +/cf-validate upload/cfempty +/cf-validate upload/cfempty/Configuration.xml ``` -`ConfigPath` авторезолв: если указана директория — ищет `Configuration.xml`. - ## Параметры | Параметр | Обяз. | Умолч. | Описание | @@ -33,7 +31,7 @@ allowed-tools: ## Команда ```powershell -powershell.exe -NoProfile -File .claude/skills/cf-validate/scripts/cf-validate.ps1 -ConfigPath "<путь>" +powershell.exe -NoProfile -File .claude/skills/cf-validate/scripts/cf-validate.ps1 -ConfigPath "upload/cfempty" ``` ## Проверки diff --git a/.claude/skills/cfe-validate/SKILL.md b/.claude/skills/cfe-validate/SKILL.md index 96a5d9ff..77fd8c92 100644 --- a/.claude/skills/cfe-validate/SKILL.md +++ b/.claude/skills/cfe-validate/SKILL.md @@ -15,12 +15,10 @@ allowed-tools: ## Использование ``` -/cfe-validate -/cfe-validate src — каталог расширения +/cfe-validate src +/cfe-validate src/Configuration.xml ``` -`ExtensionPath` авторезолв: если указана директория — ищет `Configuration.xml`. - ## Параметры | Параметр | Обяз. | Умолч. | Описание | @@ -33,7 +31,7 @@ allowed-tools: ## Команда ```powershell -powershell.exe -NoProfile -File .claude/skills/cfe-validate/scripts/cfe-validate.ps1 -ExtensionPath "<путь>" +powershell.exe -NoProfile -File .claude/skills/cfe-validate/scripts/cfe-validate.ps1 -ExtensionPath "src" ``` ## Проверки (9 шагов) diff --git a/.claude/skills/epf-validate/SKILL.md b/.claude/skills/epf-validate/SKILL.md index fa8439ff..2661386e 100644 --- a/.claude/skills/epf-validate/SKILL.md +++ b/.claude/skills/epf-validate/SKILL.md @@ -15,12 +15,10 @@ allowed-tools: ## Использование ``` -/epf-validate -/epf-validate src/МояОбработка — авторезолв в /.xml +/epf-validate src/МояОбработка +/epf-validate src/МояОбработка/МояОбработка.xml ``` -`ObjectPath` авторезолв: если указана директория — ищет `/.xml`. - ## Параметры | Параметр | Обяз. | Умолч. | Описание | @@ -33,7 +31,7 @@ allowed-tools: ## Команда ```powershell -powershell.exe -NoProfile -File .claude/skills/epf-validate/scripts/epf-validate.ps1 -ObjectPath "<путь_к_обработке>" +powershell.exe -NoProfile -File .claude/skills/epf-validate/scripts/epf-validate.ps1 -ObjectPath "src/МояОбработка" ``` ## Проверки diff --git a/.claude/skills/erf-validate/SKILL.md b/.claude/skills/erf-validate/SKILL.md index d18ddb70..17bbc6dd 100644 --- a/.claude/skills/erf-validate/SKILL.md +++ b/.claude/skills/erf-validate/SKILL.md @@ -1,7 +1,7 @@ --- name: erf-validate description: Валидация внешнего отчёта 1С (ERF). Используй после создания или модификации отчёта для проверки корректности -argument-hint: [-MaxErrors 30] +argument-hint: [-Detailed] [-MaxErrors 30] allowed-tools: - Bash - Read @@ -17,26 +17,26 @@ allowed-tools: ## Использование ``` -/erf-validate +/erf-validate src/МойОтчёт +/erf-validate src/МойОтчёт/МойОтчёт.xml ``` ## Параметры -| Параметр | Обязательный | По умолчанию | Описание | -|------------|:------------:|--------------|-------------------------------------------------| -| ObjectPath | да | — | Путь к корневому XML или каталогу отчёта | -| MaxErrors | нет | 30 | Остановиться после N ошибок | -| OutFile | нет | — | Записать результат в файл (UTF-8 BOM) | - -`ObjectPath` авторезолв: если указана директория — ищет `/.xml`. +| Параметр | Обяз. | Умолч. | Описание | +|------------|:-----:|---------|-------------------------------------------------| +| ObjectPath | да | — | Путь к корневому XML или каталогу отчёта | +| Detailed | нет | — | Показывать [OK] для каждой проверки | +| MaxErrors | нет | 30 | Остановиться после N ошибок | +| OutFile | нет | — | Записать результат в файл (UTF-8 BOM) | ## Команда ```powershell -powershell.exe -NoProfile -File .claude/skills/epf-validate/scripts/epf-validate.ps1 -ObjectPath "<путь>" +powershell.exe -NoProfile -File .claude/skills/epf-validate/scripts/epf-validate.ps1 -ObjectPath "src/МойОтчёт" ``` -## Выполняемые проверки +## Проверки | # | Проверка | Серьёзность | |----|-------------------------------------------------------|--------------| @@ -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: ERF.МойОтчёт === - -[OK] 1. Root structure: MetaDataObject/ExternalReport, version 2.17 -[OK] 2. InternalInfo: ClassId correct, 1 GeneratedType -[OK] 3. Properties: Name="МойОтчёт", Synonym present, MainDCS set -[OK] 4. ChildObjects: Form(1), Template(1) -[OK] 5. Cross-references: DefaultForm, MainDCS valid -[OK] 6. Attributes: none -[OK] 7. TabularSections: none -[OK] 8. Name uniqueness: 2 names, all unique -[OK] 9. File existence: 4 files verified -[OK] 10. Form descriptors: 1 checked - -=== Result: 0 errors, 0 warnings === -``` - -Код возврата: 0 = все проверки пройдены, 1 = есть ошибки. - -## Верификация - -``` -/erf-init --with-skd — создать отчёт с СКД -/erf-validate src/.xml — проверить результат -/erf-build — собрать ERF -``` - -## Когда использовать - -- **После `/erf-init`**: проверить scaffold -- **После добавления формы/макета/СКД**: убедиться что ChildObjects и MainDCS корректны -- **После ручного редактирования XML**: выявить структурные ошибки до сборки -- **При отладке сборки**: найти причину ошибки Designer +Exit code: 0 = OK, 1 = есть ошибки. По умолчанию краткий вывод. `-Detailed` для поштучной детализации. diff --git a/.claude/skills/form-validate/SKILL.md b/.claude/skills/form-validate/SKILL.md index c344b573..fbcb8e06 100644 --- a/.claude/skills/form-validate/SKILL.md +++ b/.claude/skills/form-validate/SKILL.md @@ -15,7 +15,7 @@ allowed-tools: ## Использование ``` -/form-validate +/form-validate Catalogs/Номенклатура/Forms/ФормаЭлемента /form-validate src/МояОбработка/Forms/Форма/Ext/Form.xml ``` @@ -30,7 +30,7 @@ allowed-tools: ## Команда ```powershell -powershell.exe -NoProfile -File .claude/skills/form-validate/scripts/form-validate.ps1 -FormPath "<.../Forms/ИмяФормы>" +powershell.exe -NoProfile -File .claude/skills/form-validate/scripts/form-validate.ps1 -FormPath "Catalogs/Номенклатура/Forms/ФормаЭлемента" ``` ## Проверки diff --git a/.claude/skills/interface-validate/SKILL.md b/.claude/skills/interface-validate/SKILL.md index b3bf8de3..bb767059 100644 --- a/.claude/skills/interface-validate/SKILL.md +++ b/.claude/skills/interface-validate/SKILL.md @@ -15,7 +15,7 @@ allowed-tools: ## Использование ``` -/interface-validate +/interface-validate Subsystems/Продажи /interface-validate Subsystems/Продажи/Ext/CommandInterface.xml ``` @@ -31,7 +31,7 @@ allowed-tools: ## Команда ```powershell -powershell.exe -NoProfile -File ".claude/skills/interface-validate/scripts/interface-validate.ps1" -CIPath "" +powershell.exe -NoProfile -File ".claude/skills/interface-validate/scripts/interface-validate.ps1" -CIPath "Subsystems/Продажи" ``` ## Проверки (13) diff --git a/.claude/skills/meta-validate/SKILL.md b/.claude/skills/meta-validate/SKILL.md index abcd59d5..49b2f6d4 100644 --- a/.claude/skills/meta-validate/SKILL.md +++ b/.claude/skills/meta-validate/SKILL.md @@ -15,12 +15,10 @@ allowed-tools: ## Использование ``` -/meta-validate -/meta-validate path1.xml|path2.xml — batch mode +/meta-validate Catalogs/Номенклатура/Номенклатура.xml +/meta-validate Catalogs/Банки|Documents/Заказ — batch mode ``` -`ObjectPath` авторезолв: если указана директория — ищет `/.xml`. - ## Параметры | Параметр | Обяз. | Умолч. | Описание | @@ -33,7 +31,7 @@ allowed-tools: ## Команда ```powershell -powershell.exe -NoProfile -File .claude/skills/meta-validate/scripts/meta-validate.ps1 -ObjectPath "" +powershell.exe -NoProfile -File .claude/skills/meta-validate/scripts/meta-validate.ps1 -ObjectPath "Catalogs/Номенклатура/Номенклатура.xml" ``` ## Поддерживаемые типы (23) diff --git a/.claude/skills/mxl-validate/SKILL.md b/.claude/skills/mxl-validate/SKILL.md index 4bdcb60c..342a2347 100644 --- a/.claude/skills/mxl-validate/SKILL.md +++ b/.claude/skills/mxl-validate/SKILL.md @@ -30,7 +30,7 @@ allowed-tools: ## Команда ```powershell -powershell.exe -NoProfile -File .claude/skills/mxl-validate/scripts/mxl-validate.ps1 -TemplatePath "<.../Templates/ИмяМакета>" +powershell.exe -NoProfile -File .claude/skills/mxl-validate/scripts/mxl-validate.ps1 -TemplatePath "Catalogs/Номенклатура/Templates/Макет" ``` ## Проверки diff --git a/.claude/skills/role-validate/SKILL.md b/.claude/skills/role-validate/SKILL.md index 6583bc96..9c1ecd58 100644 --- a/.claude/skills/role-validate/SKILL.md +++ b/.claude/skills/role-validate/SKILL.md @@ -31,7 +31,7 @@ allowed-tools: ## Команда ```powershell -powershell.exe -NoProfile -File .claude/skills/role-validate/scripts/role-validate.ps1 -RightsPath "" +powershell.exe -NoProfile -File .claude/skills/role-validate/scripts/role-validate.ps1 -RightsPath "Roles/МояРоль" ``` ## Проверки diff --git a/.claude/skills/skd-validate/SKILL.md b/.claude/skills/skd-validate/SKILL.md index f5ccc43d..440290f3 100644 --- a/.claude/skills/skd-validate/SKILL.md +++ b/.claude/skills/skd-validate/SKILL.md @@ -15,8 +15,8 @@ allowed-tools: ## Использование ``` -/skd-validate -/skd-validate path/to/Ext/Template.xml +/skd-validate src/МойОтчёт/Templates/ОсновнаяСхема +/skd-validate Catalogs/Номенклатура/Templates/СКД/Ext/Template.xml ``` ## Параметры @@ -31,7 +31,7 @@ allowed-tools: ## Команда ```powershell -powershell.exe -NoProfile -File .claude/skills/skd-validate/scripts/skd-validate.ps1 -TemplatePath "<.../Templates/ИмяМакета>" +powershell.exe -NoProfile -File .claude/skills/skd-validate/scripts/skd-validate.ps1 -TemplatePath "src/МойОтчёт/Templates/ОсновнаяСхема" ``` ## Проверки (~30) diff --git a/.claude/skills/subsystem-validate/SKILL.md b/.claude/skills/subsystem-validate/SKILL.md index 685ab238..fec286a6 100644 --- a/.claude/skills/subsystem-validate/SKILL.md +++ b/.claude/skills/subsystem-validate/SKILL.md @@ -15,7 +15,7 @@ allowed-tools: ## Использование ``` -/subsystem-validate +/subsystem-validate Subsystems/Продажи /subsystem-validate Subsystems/Продажи.xml ``` @@ -31,7 +31,7 @@ allowed-tools: ## Команда ```powershell -powershell.exe -NoProfile -File ".claude/skills/subsystem-validate/scripts/subsystem-validate.ps1" -SubsystemPath "" +powershell.exe -NoProfile -File ".claude/skills/subsystem-validate/scripts/subsystem-validate.ps1" -SubsystemPath "Subsystems/Продажи" ``` ## Проверки (13) From 903f1f27508bcf3cc5039703b5b71cab4cf92f94 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Mon, 9 Mar 2026 19:25:00 +0300 Subject: [PATCH 5/7] =?UTF-8?q?docs(validate):=20remove=20redundant=20##?= =?UTF-8?q?=20=D0=98=D1=81=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20from=20all=20SKILL.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The section duplicated ## Команда examples in slash-command format that the model never needs — it receives arguments directly. Second example paths moved into ## Команда as additional command lines. Co-Authored-By: Claude Opus 4.6 --- .claude/skills/cf-validate/SKILL.md | 8 +------- .claude/skills/cfe-validate/SKILL.md | 8 +------- .claude/skills/epf-validate/SKILL.md | 8 +------- .claude/skills/erf-validate/SKILL.md | 8 +------- .claude/skills/form-validate/SKILL.md | 8 +------- .claude/skills/interface-validate/SKILL.md | 8 +------- .claude/skills/meta-validate/SKILL.md | 8 +------- .claude/skills/mxl-validate/SKILL.md | 8 +------- .claude/skills/role-validate/SKILL.md | 6 ------ .claude/skills/skd-validate/SKILL.md | 8 +------- .claude/skills/subsystem-validate/SKILL.md | 8 +------- 11 files changed, 10 insertions(+), 76 deletions(-) diff --git a/.claude/skills/cf-validate/SKILL.md b/.claude/skills/cf-validate/SKILL.md index e46399dc..1fd71fd4 100644 --- a/.claude/skills/cf-validate/SKILL.md +++ b/.claude/skills/cf-validate/SKILL.md @@ -12,13 +12,6 @@ allowed-tools: Проверяет Configuration.xml на структурные ошибки: XML well-formedness, InternalInfo, свойства, enum-значения, ChildObjects, DefaultLanguage, файлы языков, каталоги объектов. -## Использование - -``` -/cf-validate upload/cfempty -/cf-validate upload/cfempty/Configuration.xml -``` - ## Параметры | Параметр | Обяз. | Умолч. | Описание | @@ -32,6 +25,7 @@ allowed-tools: ```powershell powershell.exe -NoProfile -File .claude/skills/cf-validate/scripts/cf-validate.ps1 -ConfigPath "upload/cfempty" +powershell.exe -NoProfile -File .claude/skills/cf-validate/scripts/cf-validate.ps1 -ConfigPath "upload/cfempty/Configuration.xml" ``` ## Проверки diff --git a/.claude/skills/cfe-validate/SKILL.md b/.claude/skills/cfe-validate/SKILL.md index 77fd8c92..9bebc2e0 100644 --- a/.claude/skills/cfe-validate/SKILL.md +++ b/.claude/skills/cfe-validate/SKILL.md @@ -12,13 +12,6 @@ allowed-tools: Проверяет структурную корректность расширения: XML-формат, свойства, состав, заимствованные объекты. Аналог `/cf-validate`, но для расширений. -## Использование - -``` -/cfe-validate src -/cfe-validate src/Configuration.xml -``` - ## Параметры | Параметр | Обяз. | Умолч. | Описание | @@ -32,6 +25,7 @@ allowed-tools: ```powershell powershell.exe -NoProfile -File .claude/skills/cfe-validate/scripts/cfe-validate.ps1 -ExtensionPath "src" +powershell.exe -NoProfile -File .claude/skills/cfe-validate/scripts/cfe-validate.ps1 -ExtensionPath "src/Configuration.xml" ``` ## Проверки (9 шагов) diff --git a/.claude/skills/epf-validate/SKILL.md b/.claude/skills/epf-validate/SKILL.md index 2661386e..cfb2c8f3 100644 --- a/.claude/skills/epf-validate/SKILL.md +++ b/.claude/skills/epf-validate/SKILL.md @@ -12,13 +12,6 @@ allowed-tools: Проверяет структурную корректность XML-исходников внешней обработки: корневую структуру, InternalInfo, свойства, ChildObjects, реквизиты, табличные части, уникальность имён, наличие файлов форм и макетов. Также работает для внешних отчётов (ERF). -## Использование - -``` -/epf-validate src/МояОбработка -/epf-validate src/МояОбработка/МояОбработка.xml -``` - ## Параметры | Параметр | Обяз. | Умолч. | Описание | @@ -32,6 +25,7 @@ allowed-tools: ```powershell powershell.exe -NoProfile -File .claude/skills/epf-validate/scripts/epf-validate.ps1 -ObjectPath "src/МояОбработка" +powershell.exe -NoProfile -File .claude/skills/epf-validate/scripts/epf-validate.ps1 -ObjectPath "src/МояОбработка/МояОбработка.xml" ``` ## Проверки diff --git a/.claude/skills/erf-validate/SKILL.md b/.claude/skills/erf-validate/SKILL.md index 17bbc6dd..48345bc5 100644 --- a/.claude/skills/erf-validate/SKILL.md +++ b/.claude/skills/erf-validate/SKILL.md @@ -14,13 +14,6 @@ allowed-tools: Использует тот же скрипт, что и `/epf-validate` — автоопределение по типу элемента (ExternalReport). -## Использование - -``` -/erf-validate src/МойОтчёт -/erf-validate src/МойОтчёт/МойОтчёт.xml -``` - ## Параметры | Параметр | Обяз. | Умолч. | Описание | @@ -34,6 +27,7 @@ allowed-tools: ```powershell powershell.exe -NoProfile -File .claude/skills/epf-validate/scripts/epf-validate.ps1 -ObjectPath "src/МойОтчёт" +powershell.exe -NoProfile -File .claude/skills/epf-validate/scripts/epf-validate.ps1 -ObjectPath "src/МойОтчёт/МойОтчёт.xml" ``` ## Проверки diff --git a/.claude/skills/form-validate/SKILL.md b/.claude/skills/form-validate/SKILL.md index fbcb8e06..0c52059d 100644 --- a/.claude/skills/form-validate/SKILL.md +++ b/.claude/skills/form-validate/SKILL.md @@ -12,13 +12,6 @@ allowed-tools: Проверяет Form.xml на структурные ошибки: уникальность ID, наличие companion-элементов, корректность ссылок DataPath и команд. -## Использование - -``` -/form-validate Catalogs/Номенклатура/Forms/ФормаЭлемента -/form-validate src/МояОбработка/Forms/Форма/Ext/Form.xml -``` - ## Параметры | Параметр | Обяз. | Умолч. | Описание | @@ -31,6 +24,7 @@ allowed-tools: ```powershell powershell.exe -NoProfile -File .claude/skills/form-validate/scripts/form-validate.ps1 -FormPath "Catalogs/Номенклатура/Forms/ФормаЭлемента" +powershell.exe -NoProfile -File .claude/skills/form-validate/scripts/form-validate.ps1 -FormPath "src/МояОбработка/Forms/Форма/Ext/Form.xml" ``` ## Проверки diff --git a/.claude/skills/interface-validate/SKILL.md b/.claude/skills/interface-validate/SKILL.md index bb767059..1f6a1386 100644 --- a/.claude/skills/interface-validate/SKILL.md +++ b/.claude/skills/interface-validate/SKILL.md @@ -12,13 +12,6 @@ allowed-tools: Проверяет XML командного интерфейса на структурные ошибки: корневой элемент, допустимые секции, порядок, формат ссылок на команды, дубликаты. -## Использование - -``` -/interface-validate Subsystems/Продажи -/interface-validate Subsystems/Продажи/Ext/CommandInterface.xml -``` - ## Параметры | Параметр | Обяз. | Умолч. | Описание | @@ -32,6 +25,7 @@ allowed-tools: ```powershell powershell.exe -NoProfile -File ".claude/skills/interface-validate/scripts/interface-validate.ps1" -CIPath "Subsystems/Продажи" +powershell.exe -NoProfile -File ".claude/skills/interface-validate/scripts/interface-validate.ps1" -CIPath "Subsystems/Продажи/Ext/CommandInterface.xml" ``` ## Проверки (13) diff --git a/.claude/skills/meta-validate/SKILL.md b/.claude/skills/meta-validate/SKILL.md index 49b2f6d4..e0e794f5 100644 --- a/.claude/skills/meta-validate/SKILL.md +++ b/.claude/skills/meta-validate/SKILL.md @@ -12,13 +12,6 @@ allowed-tools: Проверяет XML объекта метаданных из выгрузки конфигурации на структурные ошибки. -## Использование - -``` -/meta-validate Catalogs/Номенклатура/Номенклатура.xml -/meta-validate Catalogs/Банки|Documents/Заказ — batch mode -``` - ## Параметры | Параметр | Обяз. | Умолч. | Описание | @@ -32,6 +25,7 @@ allowed-tools: ```powershell powershell.exe -NoProfile -File .claude/skills/meta-validate/scripts/meta-validate.ps1 -ObjectPath "Catalogs/Номенклатура/Номенклатура.xml" +powershell.exe -NoProfile -File .claude/skills/meta-validate/scripts/meta-validate.ps1 -ObjectPath "Catalogs/Банки|Documents/Заказ" ``` ## Поддерживаемые типы (23) diff --git a/.claude/skills/mxl-validate/SKILL.md b/.claude/skills/mxl-validate/SKILL.md index 342a2347..1e680ce8 100644 --- a/.claude/skills/mxl-validate/SKILL.md +++ b/.claude/skills/mxl-validate/SKILL.md @@ -12,13 +12,6 @@ allowed-tools: Проверяет Template.xml на структурные ошибки: индексы, ссылки на палитры, диапазоны именованных областей и объединений. -## Использование - -``` -/mxl-validate Catalogs/Номенклатура/Templates/Макет -/mxl-validate src/МояОбработка/Templates/ПечатнаяФорма -``` - ## Параметры | Параметр | Обяз. | Умолч. | Описание | @@ -31,6 +24,7 @@ allowed-tools: ```powershell powershell.exe -NoProfile -File .claude/skills/mxl-validate/scripts/mxl-validate.ps1 -TemplatePath "Catalogs/Номенклатура/Templates/Макет" +powershell.exe -NoProfile -File .claude/skills/mxl-validate/scripts/mxl-validate.ps1 -TemplatePath "src/МояОбработка/Templates/ПечатнаяФорма" ``` ## Проверки diff --git a/.claude/skills/role-validate/SKILL.md b/.claude/skills/role-validate/SKILL.md index 9c1ecd58..3a66f53a 100644 --- a/.claude/skills/role-validate/SKILL.md +++ b/.claude/skills/role-validate/SKILL.md @@ -11,12 +11,6 @@ allowed-tools: Проверяет корректность `Rights.xml` роли: формат XML, namespace, глобальные флаги, типы объектов, имена прав, RLS-ограничения, шаблоны. Опционально проверяет метаданные роли (UUID, имя, синоним). -## Использование - -``` -/role-validate Roles/МояРоль -``` - ## Параметры | Параметр | Обяз. | Умолч. | Описание | diff --git a/.claude/skills/skd-validate/SKILL.md b/.claude/skills/skd-validate/SKILL.md index 440290f3..3bc20d73 100644 --- a/.claude/skills/skd-validate/SKILL.md +++ b/.claude/skills/skd-validate/SKILL.md @@ -12,13 +12,6 @@ allowed-tools: Проверяет структурную корректность Template.xml схемы компоновки данных. Выявляет ошибки формата, битые ссылки, дубликаты имён. -## Использование - -``` -/skd-validate src/МойОтчёт/Templates/ОсновнаяСхема -/skd-validate Catalogs/Номенклатура/Templates/СКД/Ext/Template.xml -``` - ## Параметры | Параметр | Обяз. | Умолч. | Описание | @@ -32,6 +25,7 @@ allowed-tools: ```powershell powershell.exe -NoProfile -File .claude/skills/skd-validate/scripts/skd-validate.ps1 -TemplatePath "src/МойОтчёт/Templates/ОсновнаяСхема" +powershell.exe -NoProfile -File .claude/skills/skd-validate/scripts/skd-validate.ps1 -TemplatePath "Catalogs/Номенклатура/Templates/СКД/Ext/Template.xml" ``` ## Проверки (~30) diff --git a/.claude/skills/subsystem-validate/SKILL.md b/.claude/skills/subsystem-validate/SKILL.md index fec286a6..2893f77e 100644 --- a/.claude/skills/subsystem-validate/SKILL.md +++ b/.claude/skills/subsystem-validate/SKILL.md @@ -12,13 +12,6 @@ allowed-tools: Проверяет структурную корректность XML-файла подсистемы из выгрузки конфигурации. -## Использование - -``` -/subsystem-validate Subsystems/Продажи -/subsystem-validate Subsystems/Продажи.xml -``` - ## Параметры | Параметр | Обяз. | Умолч. | Описание | @@ -32,6 +25,7 @@ allowed-tools: ```powershell powershell.exe -NoProfile -File ".claude/skills/subsystem-validate/scripts/subsystem-validate.ps1" -SubsystemPath "Subsystems/Продажи" +powershell.exe -NoProfile -File ".claude/skills/subsystem-validate/scripts/subsystem-validate.ps1" -SubsystemPath "Subsystems/Продажи.xml" ``` ## Проверки (13) From 9f370dd499311ae64fdbb90325cf891e9aed1542 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Mon, 9 Mar 2026 20:30:49 +0300 Subject: [PATCH 6/7] docs(role-validate): remove misleading OutFile hint for Cyrillic paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cyrillic stdout works fine — the hint caused weak models to waste an extra turn writing to file and reading it back. Verified: all 9 validate skills pass on Haiku in 4 turns each. Co-Authored-By: Claude Opus 4.6 --- .claude/skills/role-validate/SKILL.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/.claude/skills/role-validate/SKILL.md b/.claude/skills/role-validate/SKILL.md index 3a66f53a..f48b6e05 100644 --- a/.claude/skills/role-validate/SKILL.md +++ b/.claude/skills/role-validate/SKILL.md @@ -20,8 +20,6 @@ allowed-tools: | MaxErrors | нет | 30 | Макс. ошибок до остановки (по умолчанию 30) | | OutFile | нет | — | Записать результат в файл (UTF-8 BOM) | -**Важно:** Для кириллических путей используй `-OutFile` и читай результат через Read tool. - ## Команда ```powershell From d94ffdea998af2b125ed82b6a3a2e44a897824aa Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Mon, 9 Mar 2026 20:43:08 +0300 Subject: [PATCH 7/7] fix(skd-compile): quote argument-hint to fix YAML frontmatter parsing Value starting with `[` was interpreted as YAML flow sequence, breaking frontmatter parsing and the input hint in UI. Co-Authored-By: Claude Opus 4.6 --- .claude/skills/skd-compile/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.claude/skills/skd-compile/SKILL.md b/.claude/skills/skd-compile/SKILL.md index b2dc520b..c677f2d1 100644 --- a/.claude/skills/skd-compile/SKILL.md +++ b/.claude/skills/skd-compile/SKILL.md @@ -1,7 +1,7 @@ --- name: skd-compile description: Компиляция схемы компоновки данных 1С (СКД) из компактного JSON-определения. Используй когда нужно создать СКД с нуля -argument-hint: [-DefinitionFile | -Value ] -OutputPath +argument-hint: "[-DefinitionFile | -Value ] -OutputPath " allowed-tools: - Bash - Read