From ffdee04a9570074dd1123b40d541adf36c764998 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Mon, 9 Mar 2026 19:07:16 +0300 Subject: [PATCH] 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) | # | Проверка | Серьёзность |