mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-06-15 10:24:57 +03:00
refactor(validate): auto-detect metadata in role-validate, clean up SKILL.md
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 <noreply@anthropic.com>
This commit is contained in:
@@ -33,8 +33,6 @@ allowed-tools:
|
||||
powershell.exe -NoProfile -File .claude/skills/form-validate/scripts/form-validate.ps1 -FormPath "<.../Forms/ИмяФормы>"
|
||||
```
|
||||
|
||||
Можно указать директорию формы — скрипт найдёт Ext/Form.xml автоматически.
|
||||
|
||||
## Проверки
|
||||
|
||||
| # | Проверка | Серьёзность |
|
||||
|
||||
@@ -34,8 +34,6 @@ allowed-tools:
|
||||
powershell.exe -NoProfile -File ".claude/skills/interface-validate/scripts/interface-validate.ps1" -CIPath "<Subsystems/ИмяПодсистемы>"
|
||||
```
|
||||
|
||||
Можно указать директорию подсистемы — скрипт найдёт Ext/CommandInterface.xml автоматически.
|
||||
|
||||
## Проверки (13)
|
||||
|
||||
| # | Проверка | Серьёзность |
|
||||
|
||||
@@ -15,31 +15,24 @@ allowed-tools:
|
||||
## Использование
|
||||
|
||||
```
|
||||
/mxl-validate <TemplatePath>
|
||||
/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 автоматически.
|
||||
|
||||
## Проверки
|
||||
|
||||
| # | Проверка | Серьёзность |
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: role-validate
|
||||
description: Валидация роли 1С. Используй после создания или модификации роли для проверки корректности
|
||||
argument-hint: <RightsPath> [-Detailed] [-MaxErrors 30] [-MetadataPath <path>]
|
||||
argument-hint: <RightsPath> [-Detailed] [-MaxErrors 30]
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
@@ -14,16 +14,14 @@ allowed-tools:
|
||||
## Использование
|
||||
|
||||
```
|
||||
/role-validate <RightsPath>
|
||||
/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 "<Roles/ИмяРоли>" [-MetadataPath "<path>"]
|
||||
powershell.exe -NoProfile -File .claude/skills/role-validate/scripts/role-validate.ps1 -RightsPath "<Roles/ИмяРоли>"
|
||||
```
|
||||
|
||||
Можно указать директорию роли — скрипт найдёт Ext/Rights.xml автоматически.
|
||||
|
||||
## Проверки
|
||||
|
||||
| # | Проверка | Серьёзность |
|
||||
|
||||
@@ -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: <Role> 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: <Role> 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: <Name> is empty or missing"
|
||||
}
|
||||
|
||||
$synNode = $roleNode.SelectSingleNode(".//*[local-name()='Synonym']")
|
||||
if ($synNode -and $synNode.InnerXml) {
|
||||
Report-OK "Metadata: Synonym present"
|
||||
} else {
|
||||
Report-Warn "Metadata: <Synonym> 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: <Name> is empty or missing"
|
||||
}
|
||||
|
||||
$synNode = $roleNode.SelectSingleNode(".//*[local-name()='Synonym']")
|
||||
if ($synNode -and $synNode.InnerXml) {
|
||||
Report-OK "Metadata: Synonym present"
|
||||
} else {
|
||||
Report-Warn "Metadata: <Synonym> 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)
|
||||
|
||||
@@ -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 <Role> 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 <Role> 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: <Role> 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: <Role> 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: <Name> 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: <Name> 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: <Synonym> 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: <Synonym> 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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
| Группа | Что проверяется |
|
||||
|
||||
@@ -34,8 +34,6 @@ allowed-tools:
|
||||
powershell.exe -NoProfile -File ".claude/skills/subsystem-validate/scripts/subsystem-validate.ps1" -SubsystemPath "<Subsystems/ИмяПодсистемы>"
|
||||
```
|
||||
|
||||
Можно указать директорию подсистемы — скрипт найдёт XML-файл автоматически.
|
||||
|
||||
## Проверки (13)
|
||||
|
||||
| # | Проверка | Серьёзность |
|
||||
|
||||
Reference in New Issue
Block a user