From 489b8389aac335c6b877652981629615060c0a74 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Sat, 20 Jun 2026 14:14:47 +0300 Subject: [PATCH] =?UTF-8?q?feat(form-info,skd-info,role-info,subsystem-inf?= =?UTF-8?q?o,mxl-info):=20=D1=81=D1=82=D1=80=D0=BE=D0=BA=D0=B0=20=D1=81?= =?UTF-8?q?=D0=BE=D1=81=D1=82=D0=BE=D1=8F=D0=BD=D0=B8=D1=8F=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=B4=D0=B4=D0=B5=D1=80=D0=B6=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Размножение per-object паттерна состояния поддержки из meta-info на остальные info-навыки. Унифицированный helper Get-SupportStatusForPath (нативная копия в каждом скрипте): walk-up от пути цели — берёт uuid ближайшего метафайла элемента (форма/макет/роль/подсистема, либо сам целевой .xml) и корень конфигурации с ParentConfigurations.bin, затем G-vs-f1 и консервативная свёртка min(f1) по блокам поставщиков. Резолв точный на уровне элемента: форма с индивидуально включённым редактированием (Валюты.ФормаСписка f1=1) показывает «редактируется», а форма того же объекта на замке (ФормаЭлемента f1=0) — «на замке». form-info v1.4, skd-info v1.7, role-info/subsystem-info/mxl-info v1.1. Проверено на корпусе (acc G=1 → read-only, erp → снято) — оба порта байт-в-байт. Тесты: все 7 info-навыков зелёные на PowerShell и Python. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../skills/form-info/scripts/form-info.ps1 | 66 +++++++++++++++- .claude/skills/form-info/scripts/form-info.py | 75 ++++++++++++++++++- .claude/skills/mxl-info/scripts/mxl-info.ps1 | 62 ++++++++++++++- .claude/skills/mxl-info/scripts/mxl-info.py | 71 +++++++++++++++++- .../skills/role-info/scripts/role-info.ps1 | 62 ++++++++++++++- .claude/skills/role-info/scripts/role-info.py | 72 +++++++++++++++++- .claude/skills/skd-info/scripts/skd-info.ps1 | 62 ++++++++++++++- .claude/skills/skd-info/scripts/skd-info.py | 71 +++++++++++++++++- .../subsystem-info/scripts/subsystem-info.ps1 | 62 ++++++++++++++- .../subsystem-info/scripts/subsystem-info.py | 71 +++++++++++++++++- .../cases/form-info/real-acc-support.json | 6 ++ .../cases/mxl-info/real-acc-support.json | 6 ++ .../cases/role-info/real-acc-support.json | 6 ++ .../cases/skd-info/real-acc-support.json | 6 ++ .../subsystem-info/real-acc-support.json | 6 ++ 15 files changed, 694 insertions(+), 10 deletions(-) create mode 100644 tests/skills/cases/form-info/real-acc-support.json create mode 100644 tests/skills/cases/mxl-info/real-acc-support.json create mode 100644 tests/skills/cases/role-info/real-acc-support.json create mode 100644 tests/skills/cases/skd-info/real-acc-support.json create mode 100644 tests/skills/cases/subsystem-info/real-acc-support.json diff --git a/.claude/skills/form-info/scripts/form-info.ps1 b/.claude/skills/form-info/scripts/form-info.ps1 index b7f5b790..7b9e36cc 100644 --- a/.claude/skills/form-info/scripts/form-info.ps1 +++ b/.claude/skills/form-info/scripts/form-info.ps1 @@ -1,4 +1,4 @@ -# form-info v1.3 — Analyze 1C managed form structure +# form-info v1.4 — Analyze 1C managed form structure # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory=$true)] @@ -368,6 +368,69 @@ if ($formsIdx -ge 0 -and ($formsIdx + 1) -lt $parts.Count) { } } +# --- Support status (Ext/ParentConfigurations.bin) --- +# See docs/1c-support-state-spec.md. Walks up from the target path, taking the +# uuid of the nearest element meta-xml (form/template/etc.) and the config root +# bin. Never throws — degrades to "не на поддержке". +function Get-SupportStatusForPath([string]$targetPath) { + try { + $rp = (Resolve-Path $targetPath).Path + $elemUuid = $null + $binPath = $null + # Reads the uuid of the first metadata element in an .xml file (or $null). + function Get-RootUuid([string]$xmlPath) { + if (-not (Test-Path $xmlPath)) { return $null } + try { + [xml]$mx = Get-Content -Path $xmlPath -Encoding UTF8 + $el = $mx.DocumentElement.FirstChild + while ($el -and $el.NodeType -ne 'Element') { $el = $el.NextSibling } + if ($el) { $u = $el.GetAttribute("uuid"); if ($u) { return $u } } + } catch {} + return $null + } + # The target file itself may be the element meta-xml (e.g. Subsystems/X.xml). + $elemUuid = Get-RootUuid $rp + $d = [System.IO.Path]::GetDirectoryName($rp) + for ($i = 0; $i -lt 12 -and $d; $i++) { + if (-not $elemUuid) { $elemUuid = Get-RootUuid "$d.xml" } + if (-not $binPath) { + $cand = Join-Path (Join-Path $d "Ext") "ParentConfigurations.bin" + if ((Test-Path $cand) -or (Test-Path (Join-Path $d "Configuration.xml"))) { $binPath = $cand } + } + if ($elemUuid -and $binPath) { break } + $parent = [System.IO.Path]::GetDirectoryName($d) + if ($parent -eq $d) { break } + $d = $parent + } + if (-not $binPath -or -not (Test-Path $binPath)) { return "не на поддержке" } + $bytes = [System.IO.File]::ReadAllBytes($binPath) + if ($bytes.Length -le 32) { return "снято с поддержки (правки свободны)" } + $start = 0 + if ($bytes.Length -ge 3 -and $bytes[0] -eq 0xEF -and $bytes[1] -eq 0xBB -and $bytes[2] -eq 0xBF) { $start = 3 } + $text = [System.Text.Encoding]::UTF8.GetString($bytes, $start, $bytes.Length - $start) + $h = [regex]::Match($text, '^\{6,(\d+),(\d+),') + if (-not $h.Success) { return "не на поддержке" } + $G = [int]$h.Groups[1].Value + $K = [int]$h.Groups[2].Value + if ($K -eq 0) { return "снято с поддержки (правки свободны)" } + if ($G -eq 1) { return "конфигурация read-only (возможность изменения выключена) — правки невозможны без включения" } + if (-not $elemUuid) { return "не на поддержке" } + $u = [regex]::Escape($elemUuid.ToLower()) + $best = $null + foreach ($m in [regex]::Matches($text, "([0-2]),0,$u")) { + $f1 = [int]$m.Groups[1].Value + if ($null -eq $best -or $f1 -lt $best) { $best = $f1 } + } + if ($null -eq $best) { return "не на поддержке" } + switch ($best) { + 0 { return "на замке — прямая правка сломает обновления; дорабатывай через cfe-* либо включи редактирование объекта" } + 1 { return "редактируется с сохранением поддержки" } + 2 { return "снято с поддержки (правки свободны)" } + } + return "не на поддержке" + } catch { return "не на поддержке" } +} + # --- Collect output --- $lines = @() @@ -385,6 +448,7 @@ if ($formTitle) { $header += " — `"$formTitle`"" } if ($objectContext) { $header += " ($objectContext)" } $header += " ===" $lines += $header +$lines += "Поддержка: $(Get-SupportStatusForPath $FormPath)" # --- Form properties (Title excluded — shown in header) --- diff --git a/.claude/skills/form-info/scripts/form-info.py b/.claude/skills/form-info/scripts/form-info.py index 8599cc47..f7b9b70d 100644 --- a/.claude/skills/form-info/scripts/form-info.py +++ b/.claude/skills/form-info/scripts/form-info.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# form-info v1.3 — Analyze 1C managed form structure +# form-info v1.4 — Analyze 1C managed form structure # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -336,6 +336,78 @@ def build_tree(child_items_node, prefix, tree_lines, expand="", state=None): build_tree(ci, prefix + continuation, tree_lines, expand, state) +# --- Support status (Ext/ParentConfigurations.bin) --- +# See docs/1c-support-state-spec.md. Walks up from the target path, taking the +# uuid of the nearest element meta-xml and the config root bin. Never throws — +# degrades to "не на поддержке". +def get_support_status_for_path(target_path): + try: + def root_uuid(xml_path): + if not os.path.isfile(xml_path): + return None + try: + mx = etree.parse(xml_path).getroot() + for child in mx: + if isinstance(child.tag, str) and child.get("uuid"): + return child.get("uuid") + except Exception: + pass + return None + rp = os.path.abspath(target_path) + # The target file itself may be the element meta-xml (e.g. Subsystems/X.xml). + elem_uuid = root_uuid(rp) + bin_path = None + d = os.path.dirname(rp) + for _ in range(12): + if not d: + break + if not elem_uuid: + elem_uuid = root_uuid(d + ".xml") + if not bin_path: + cand = os.path.join(d, "Ext", "ParentConfigurations.bin") + if os.path.exists(cand) or os.path.exists(os.path.join(d, "Configuration.xml")): + bin_path = cand + if elem_uuid and bin_path: + break + parent = os.path.dirname(d) + if parent == d: + break + d = parent + if not bin_path or not os.path.exists(bin_path): + return "не на поддержке" + data = open(bin_path, "rb").read() + if len(data) <= 32: + return "снято с поддержки (правки свободны)" + if data[:3] == b"\xef\xbb\xbf": + data = data[3:] + text = data.decode("utf-8", "replace") + h = re.match(r"\{6,(\d+),(\d+),", text) + if not h: + return "не на поддержке" + g = int(h.group(1)) + k = int(h.group(2)) + if k == 0: + return "снято с поддержки (правки свободны)" + if g == 1: + return "конфигурация read-only (возможность изменения выключена) — правки невозможны без включения" + if not elem_uuid: + return "не на поддержке" + best = None + for m in re.finditer(r"([0-2]),0," + re.escape(elem_uuid.lower()), text): + f1 = int(m.group(1)) + if best is None or f1 < best: + best = f1 + if best is None: + return "не на поддержке" + return { + 0: "на замке — прямая правка сломает обновления; дорабатывай через cfe-* либо включи редактирование объекта", + 1: "редактируется с сохранением поддержки", + 2: "снято с поддержки (правки свободны)", + }.get(best, "не на поддержке") + except Exception: + return "не на поддержке" + + # --- Main --- def main(): @@ -441,6 +513,7 @@ def main(): header += f" ({object_context})" header += " ===" lines.append(header) + lines.append(f"Поддержка: {get_support_status_for_path(form_path)}") # --- Form properties (Title excluded -- shown in header) --- prop_names = [ diff --git a/.claude/skills/mxl-info/scripts/mxl-info.ps1 b/.claude/skills/mxl-info/scripts/mxl-info.ps1 index ef1dda07..44ea6bb8 100644 --- a/.claude/skills/mxl-info/scripts/mxl-info.ps1 +++ b/.claude/skills/mxl-info/scripts/mxl-info.ps1 @@ -1,4 +1,4 @@ -# mxl-info v1.0 — Analyze 1C spreadsheet structure +# mxl-info v1.1 — Analyze 1C spreadsheet structure # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Alias('Path')] @@ -321,11 +321,71 @@ if ($Format -eq "json") { exit 0 } +function Get-SupportStatusForPath([string]$targetPath) { + try { + $rp = (Resolve-Path $targetPath).Path + $elemUuid = $null + $binPath = $null + # Reads the uuid of the first metadata element in an .xml file (or $null). + function Get-RootUuid([string]$xmlPath) { + if (-not (Test-Path $xmlPath)) { return $null } + try { + [xml]$mx = Get-Content -Path $xmlPath -Encoding UTF8 + $el = $mx.DocumentElement.FirstChild + while ($el -and $el.NodeType -ne 'Element') { $el = $el.NextSibling } + if ($el) { $u = $el.GetAttribute("uuid"); if ($u) { return $u } } + } catch {} + return $null + } + # The target file itself may be the element meta-xml (e.g. Subsystems/X.xml). + $elemUuid = Get-RootUuid $rp + $d = [System.IO.Path]::GetDirectoryName($rp) + for ($i = 0; $i -lt 12 -and $d; $i++) { + if (-not $elemUuid) { $elemUuid = Get-RootUuid "$d.xml" } + if (-not $binPath) { + $cand = Join-Path (Join-Path $d "Ext") "ParentConfigurations.bin" + if ((Test-Path $cand) -or (Test-Path (Join-Path $d "Configuration.xml"))) { $binPath = $cand } + } + if ($elemUuid -and $binPath) { break } + $parent = [System.IO.Path]::GetDirectoryName($d) + if ($parent -eq $d) { break } + $d = $parent + } + if (-not $binPath -or -not (Test-Path $binPath)) { return "не на поддержке" } + $bytes = [System.IO.File]::ReadAllBytes($binPath) + if ($bytes.Length -le 32) { return "снято с поддержки (правки свободны)" } + $start = 0 + if ($bytes.Length -ge 3 -and $bytes[0] -eq 0xEF -and $bytes[1] -eq 0xBB -and $bytes[2] -eq 0xBF) { $start = 3 } + $text = [System.Text.Encoding]::UTF8.GetString($bytes, $start, $bytes.Length - $start) + $h = [regex]::Match($text, '^\{6,(\d+),(\d+),') + if (-not $h.Success) { return "не на поддержке" } + $G = [int]$h.Groups[1].Value + $K = [int]$h.Groups[2].Value + if ($K -eq 0) { return "снято с поддержки (правки свободны)" } + if ($G -eq 1) { return "конфигурация read-only (возможность изменения выключена) — правки невозможны без включения" } + if (-not $elemUuid) { return "не на поддержке" } + $u = [regex]::Escape($elemUuid.ToLower()) + $best = $null + foreach ($m in [regex]::Matches($text, "([0-2]),0,$u")) { + $f1 = [int]$m.Groups[1].Value + if ($null -eq $best -or $f1 -lt $best) { $best = $f1 } + } + if ($null -eq $best) { return "не на поддержке" } + switch ($best) { + 0 { return "на замке — прямая правка сломает обновления; дорабатывай через cfe-* либо включи редактирование объекта" } + 1 { return "редактируется с сохранением поддержки" } + 2 { return "снято с поддержки (правки свободны)" } + } + return "не на поддержке" + } catch { return "не на поддержке" } +} + # --- Text format output --- $lines = @() $lines += "=== $templateName ===" +$lines += "Поддержка: $(Get-SupportStatusForPath $TemplatePath)" $lines += " Rows: $docHeight, Columns: $defaultColCount" if ($columnSets.Count -eq 0) { diff --git a/.claude/skills/mxl-info/scripts/mxl-info.py b/.claude/skills/mxl-info/scripts/mxl-info.py index fe235e27..716a7bfd 100644 --- a/.claude/skills/mxl-info/scripts/mxl-info.py +++ b/.claude/skills/mxl-info/scripts/mxl-info.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# mxl-info v1.0 — Analyze 1C spreadsheet structure +# mxl-info v1.1 — Analyze 1C spreadsheet structure # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -308,10 +308,79 @@ if args.Format == "json": print(json.dumps(result, ensure_ascii=False, indent=2)) sys.exit(0) +def get_support_status_for_path(target_path): + try: + def root_uuid(xml_path): + if not os.path.isfile(xml_path): + return None + try: + mx = etree.parse(xml_path).getroot() + for child in mx: + if isinstance(child.tag, str) and child.get("uuid"): + return child.get("uuid") + except Exception: + pass + return None + rp = os.path.abspath(target_path) + # The target file itself may be the element meta-xml (e.g. Subsystems/X.xml). + elem_uuid = root_uuid(rp) + bin_path = None + d = os.path.dirname(rp) + for _ in range(12): + if not d: + break + if not elem_uuid: + elem_uuid = root_uuid(d + ".xml") + if not bin_path: + cand = os.path.join(d, "Ext", "ParentConfigurations.bin") + if os.path.exists(cand) or os.path.exists(os.path.join(d, "Configuration.xml")): + bin_path = cand + if elem_uuid and bin_path: + break + parent = os.path.dirname(d) + if parent == d: + break + d = parent + if not bin_path or not os.path.exists(bin_path): + return "не на поддержке" + data = open(bin_path, "rb").read() + if len(data) <= 32: + return "снято с поддержки (правки свободны)" + if data[:3] == b"\xef\xbb\xbf": + data = data[3:] + text = data.decode("utf-8", "replace") + h = re.match(r"\{6,(\d+),(\d+),", text) + if not h: + return "не на поддержке" + g = int(h.group(1)) + k = int(h.group(2)) + if k == 0: + return "снято с поддержки (правки свободны)" + if g == 1: + return "конфигурация read-only (возможность изменения выключена) — правки невозможны без включения" + if not elem_uuid: + return "не на поддержке" + best = None + for m in re.finditer(r"([0-2]),0," + re.escape(elem_uuid.lower()), text): + f1 = int(m.group(1)) + if best is None or f1 < best: + best = f1 + if best is None: + return "не на поддержке" + return { + 0: "на замке — прямая правка сломает обновления; дорабатывай через cfe-* либо включи редактирование объекта", + 1: "редактируется с сохранением поддержки", + 2: "снято с поддержки (правки свободны)", + }.get(best, "не на поддержке") + except Exception: + return "не на поддержке" + + # --- Text format output --- lines = [] lines.append(f"=== {template_name} ===") +lines.append(f"Поддержка: {get_support_status_for_path(template_path)}") lines.append(f" Rows: {doc_height}, Columns: {default_col_count}") if len(column_sets) == 0: diff --git a/.claude/skills/role-info/scripts/role-info.ps1 b/.claude/skills/role-info/scripts/role-info.ps1 index ed6c5bb9..46b8d324 100644 --- a/.claude/skills/role-info/scripts/role-info.ps1 +++ b/.claude/skills/role-info/scripts/role-info.ps1 @@ -1,4 +1,4 @@ -# role-info v1.0 — Analyze 1C role rights +# role-info v1.1 — Analyze 1C role rights # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory=$true)][Alias('Path')][string]$RightsPath, @@ -145,11 +145,71 @@ foreach ($tpl in $tplNodes) { } } +function Get-SupportStatusForPath([string]$targetPath) { + try { + $rp = (Resolve-Path $targetPath).Path + $elemUuid = $null + $binPath = $null + # Reads the uuid of the first metadata element in an .xml file (or $null). + function Get-RootUuid([string]$xmlPath) { + if (-not (Test-Path $xmlPath)) { return $null } + try { + [xml]$mx = Get-Content -Path $xmlPath -Encoding UTF8 + $el = $mx.DocumentElement.FirstChild + while ($el -and $el.NodeType -ne 'Element') { $el = $el.NextSibling } + if ($el) { $u = $el.GetAttribute("uuid"); if ($u) { return $u } } + } catch {} + return $null + } + # The target file itself may be the element meta-xml (e.g. Subsystems/X.xml). + $elemUuid = Get-RootUuid $rp + $d = [System.IO.Path]::GetDirectoryName($rp) + for ($i = 0; $i -lt 12 -and $d; $i++) { + if (-not $elemUuid) { $elemUuid = Get-RootUuid "$d.xml" } + if (-not $binPath) { + $cand = Join-Path (Join-Path $d "Ext") "ParentConfigurations.bin" + if ((Test-Path $cand) -or (Test-Path (Join-Path $d "Configuration.xml"))) { $binPath = $cand } + } + if ($elemUuid -and $binPath) { break } + $parent = [System.IO.Path]::GetDirectoryName($d) + if ($parent -eq $d) { break } + $d = $parent + } + if (-not $binPath -or -not (Test-Path $binPath)) { return "не на поддержке" } + $bytes = [System.IO.File]::ReadAllBytes($binPath) + if ($bytes.Length -le 32) { return "снято с поддержки (правки свободны)" } + $start = 0 + if ($bytes.Length -ge 3 -and $bytes[0] -eq 0xEF -and $bytes[1] -eq 0xBB -and $bytes[2] -eq 0xBF) { $start = 3 } + $text = [System.Text.Encoding]::UTF8.GetString($bytes, $start, $bytes.Length - $start) + $h = [regex]::Match($text, '^\{6,(\d+),(\d+),') + if (-not $h.Success) { return "не на поддержке" } + $G = [int]$h.Groups[1].Value + $K = [int]$h.Groups[2].Value + if ($K -eq 0) { return "снято с поддержки (правки свободны)" } + if ($G -eq 1) { return "конфигурация read-only (возможность изменения выключена) — правки невозможны без включения" } + if (-not $elemUuid) { return "не на поддержке" } + $u = [regex]::Escape($elemUuid.ToLower()) + $best = $null + foreach ($m in [regex]::Matches($text, "([0-2]),0,$u")) { + $f1 = [int]$m.Groups[1].Value + if ($null -eq $best -or $f1 -lt $best) { $best = $f1 } + } + if ($null -eq $best) { return "не на поддержке" } + switch ($best) { + 0 { return "на замке — прямая правка сломает обновления; дорабатывай через cfe-* либо включи редактирование объекта" } + 1 { return "редактируется с сохранением поддержки" } + 2 { return "снято с поддержки (правки свободны)" } + } + return "не на поддержке" + } catch { return "не на поддержке" } +} + # --- Output --- $header = "=== Role: $roleName" if ($roleSynonym) { $header += " --- `"$roleSynonym`"" } $header += " ===" Out $header +Out "Поддержка: $(Get-SupportStatusForPath $RightsPath)" Out "" Out "Properties: setForNewObjects=$setForNew, setForAttributesByDefault=$setForAttrs, independentRightsOfChildObjects=$independentChild" diff --git a/.claude/skills/role-info/scripts/role-info.py b/.claude/skills/role-info/scripts/role-info.py index 4ea4f9d3..a15e759f 100644 --- a/.claude/skills/role-info/scripts/role-info.py +++ b/.claude/skills/role-info/scripts/role-info.py @@ -1,9 +1,10 @@ #!/usr/bin/env python3 -# role-info v1.0 — Analyze 1C role rights +# role-info v1.1 — Analyze 1C role rights # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse import os +import re import sys from collections import OrderedDict from lxml import etree @@ -147,12 +148,81 @@ for tpl in root.findall("r:restrictionTemplate", NSMAP): t_name = t_name[:paren_idx] templates.append(t_name) +def get_support_status_for_path(target_path): + try: + def root_uuid(xml_path): + if not os.path.isfile(xml_path): + return None + try: + mx = etree.parse(xml_path).getroot() + for child in mx: + if isinstance(child.tag, str) and child.get("uuid"): + return child.get("uuid") + except Exception: + pass + return None + rp = os.path.abspath(target_path) + # The target file itself may be the element meta-xml (e.g. Subsystems/X.xml). + elem_uuid = root_uuid(rp) + bin_path = None + d = os.path.dirname(rp) + for _ in range(12): + if not d: + break + if not elem_uuid: + elem_uuid = root_uuid(d + ".xml") + if not bin_path: + cand = os.path.join(d, "Ext", "ParentConfigurations.bin") + if os.path.exists(cand) or os.path.exists(os.path.join(d, "Configuration.xml")): + bin_path = cand + if elem_uuid and bin_path: + break + parent = os.path.dirname(d) + if parent == d: + break + d = parent + if not bin_path or not os.path.exists(bin_path): + return "не на поддержке" + data = open(bin_path, "rb").read() + if len(data) <= 32: + return "снято с поддержки (правки свободны)" + if data[:3] == b"\xef\xbb\xbf": + data = data[3:] + text = data.decode("utf-8", "replace") + h = re.match(r"\{6,(\d+),(\d+),", text) + if not h: + return "не на поддержке" + g = int(h.group(1)) + k = int(h.group(2)) + if k == 0: + return "снято с поддержки (правки свободны)" + if g == 1: + return "конфигурация read-only (возможность изменения выключена) — правки невозможны без включения" + if not elem_uuid: + return "не на поддержке" + best = None + for m in re.finditer(r"([0-2]),0," + re.escape(elem_uuid.lower()), text): + f1 = int(m.group(1)) + if best is None or f1 < best: + best = f1 + if best is None: + return "не на поддержке" + return { + 0: "на замке — прямая правка сломает обновления; дорабатывай через cfe-* либо включи редактирование объекта", + 1: "редактируется с сохранением поддержки", + 2: "снято с поддержки (правки свободны)", + }.get(best, "не на поддержке") + except Exception: + return "не на поддержке" + + # --- Output --- header = f"=== Role: {role_name}" if role_synonym: header += f' --- "{role_synonym}"' header += " ===" out(header) +out(f"Поддержка: {get_support_status_for_path(rights_path)}") out() out(f"Properties: setForNewObjects={set_for_new}, setForAttributesByDefault={set_for_attrs}, independentRightsOfChildObjects={independent_child}") diff --git a/.claude/skills/skd-info/scripts/skd-info.ps1 b/.claude/skills/skd-info/scripts/skd-info.ps1 index 066b0d0e..8f7cf5e3 100644 --- a/.claude/skills/skd-info/scripts/skd-info.ps1 +++ b/.claude/skills/skd-info/scripts/skd-info.ps1 @@ -1,4 +1,4 @@ -# skd-info v1.6 — Analyze 1C DCS structure +# skd-info v1.7 — Analyze 1C DCS structure # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory=$true)] @@ -334,8 +334,68 @@ for ($i = $pathParts.Count - 1; $i -ge 0; $i--) { $totalXmlLines = (Get-Content $resolvedPath).Count +function Get-SupportStatusForPath([string]$targetPath) { + try { + $rp = (Resolve-Path $targetPath).Path + $elemUuid = $null + $binPath = $null + # Reads the uuid of the first metadata element in an .xml file (or $null). + function Get-RootUuid([string]$xmlPath) { + if (-not (Test-Path $xmlPath)) { return $null } + try { + [xml]$mx = Get-Content -Path $xmlPath -Encoding UTF8 + $el = $mx.DocumentElement.FirstChild + while ($el -and $el.NodeType -ne 'Element') { $el = $el.NextSibling } + if ($el) { $u = $el.GetAttribute("uuid"); if ($u) { return $u } } + } catch {} + return $null + } + # The target file itself may be the element meta-xml (e.g. Subsystems/X.xml). + $elemUuid = Get-RootUuid $rp + $d = [System.IO.Path]::GetDirectoryName($rp) + for ($i = 0; $i -lt 12 -and $d; $i++) { + if (-not $elemUuid) { $elemUuid = Get-RootUuid "$d.xml" } + if (-not $binPath) { + $cand = Join-Path (Join-Path $d "Ext") "ParentConfigurations.bin" + if ((Test-Path $cand) -or (Test-Path (Join-Path $d "Configuration.xml"))) { $binPath = $cand } + } + if ($elemUuid -and $binPath) { break } + $parent = [System.IO.Path]::GetDirectoryName($d) + if ($parent -eq $d) { break } + $d = $parent + } + if (-not $binPath -or -not (Test-Path $binPath)) { return "не на поддержке" } + $bytes = [System.IO.File]::ReadAllBytes($binPath) + if ($bytes.Length -le 32) { return "снято с поддержки (правки свободны)" } + $start = 0 + if ($bytes.Length -ge 3 -and $bytes[0] -eq 0xEF -and $bytes[1] -eq 0xBB -and $bytes[2] -eq 0xBF) { $start = 3 } + $text = [System.Text.Encoding]::UTF8.GetString($bytes, $start, $bytes.Length - $start) + $h = [regex]::Match($text, '^\{6,(\d+),(\d+),') + if (-not $h.Success) { return "не на поддержке" } + $G = [int]$h.Groups[1].Value + $K = [int]$h.Groups[2].Value + if ($K -eq 0) { return "снято с поддержки (правки свободны)" } + if ($G -eq 1) { return "конфигурация read-only (возможность изменения выключена) — правки невозможны без включения" } + if (-not $elemUuid) { return "не на поддержке" } + $u = [regex]::Escape($elemUuid.ToLower()) + $best = $null + foreach ($m in [regex]::Matches($text, "([0-2]),0,$u")) { + $f1 = [int]$m.Groups[1].Value + if ($null -eq $best -or $f1 -lt $best) { $best = $f1 } + } + if ($null -eq $best) { return "не на поддержке" } + switch ($best) { + 0 { return "на замке — прямая правка сломает обновления; дорабатывай через cfe-* либо включи редактирование объекта" } + 1 { return "редактируется с сохранением поддержки" } + 2 { return "снято с поддержки (правки свободны)" } + } + return "не на поддержке" + } catch { return "не на поддержке" } +} + function Show-Overview { $lines.Add("=== DCS: $templateName ($totalXmlLines lines) ===") + $lines.Add("Поддержка: $(Get-SupportStatusForPath $TemplatePath)") $lines.Add("") # Sources diff --git a/.claude/skills/skd-info/scripts/skd-info.py b/.claude/skills/skd-info/scripts/skd-info.py index fda5e375..8821dd77 100644 --- a/.claude/skills/skd-info/scripts/skd-info.py +++ b/.claude/skills/skd-info/scripts/skd-info.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# skd-info v1.6 — Analyze 1C DCS structure +# skd-info v1.7 — Analyze 1C DCS structure # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -265,6 +265,74 @@ def build_structure_tree(item_node, prefix, is_last, out_lines): build_structure_tree(child, f"{prefix}{continuation}", last, out_lines) +def get_support_status_for_path(target_path): + try: + def root_uuid(xml_path): + if not os.path.isfile(xml_path): + return None + try: + mx = etree.parse(xml_path).getroot() + for child in mx: + if isinstance(child.tag, str) and child.get("uuid"): + return child.get("uuid") + except Exception: + pass + return None + rp = os.path.abspath(target_path) + # The target file itself may be the element meta-xml (e.g. Subsystems/X.xml). + elem_uuid = root_uuid(rp) + bin_path = None + d = os.path.dirname(rp) + for _ in range(12): + if not d: + break + if not elem_uuid: + elem_uuid = root_uuid(d + ".xml") + if not bin_path: + cand = os.path.join(d, "Ext", "ParentConfigurations.bin") + if os.path.exists(cand) or os.path.exists(os.path.join(d, "Configuration.xml")): + bin_path = cand + if elem_uuid and bin_path: + break + parent = os.path.dirname(d) + if parent == d: + break + d = parent + if not bin_path or not os.path.exists(bin_path): + return "не на поддержке" + data = open(bin_path, "rb").read() + if len(data) <= 32: + return "снято с поддержки (правки свободны)" + if data[:3] == b"\xef\xbb\xbf": + data = data[3:] + text = data.decode("utf-8", "replace") + h = re.match(r"\{6,(\d+),(\d+),", text) + if not h: + return "не на поддержке" + g = int(h.group(1)) + k = int(h.group(2)) + if k == 0: + return "снято с поддержки (правки свободны)" + if g == 1: + return "конфигурация read-only (возможность изменения выключена) — правки невозможны без включения" + if not elem_uuid: + return "не на поддержке" + best = None + for m in re.finditer(r"([0-2]),0," + re.escape(elem_uuid.lower()), text): + f1 = int(m.group(1)) + if best is None or f1 < best: + best = f1 + if best is None: + return "не на поддержке" + return { + 0: "на замке — прямая правка сломает обновления; дорабатывай через cfe-* либо включи редактирование объекта", + 1: "редактируется с сохранением поддержки", + 2: "снято с поддержки (правки свободны)", + }.get(best, "не на поддержке") + except Exception: + return "не на поддержке" + + def main(): sys.stdout.reconfigure(encoding="utf-8") sys.stderr.reconfigure(encoding="utf-8") @@ -356,6 +424,7 @@ def main(): def show_overview(): lines.append(f"=== DCS: {template_name} ({total_xml_lines} lines) ===") + lines.append(f"Поддержка: {get_support_status_for_path(template_path)}") lines.append("") # Sources diff --git a/.claude/skills/subsystem-info/scripts/subsystem-info.ps1 b/.claude/skills/subsystem-info/scripts/subsystem-info.ps1 index 3d26cd7b..3c031849 100644 --- a/.claude/skills/subsystem-info/scripts/subsystem-info.ps1 +++ b/.claude/skills/subsystem-info/scripts/subsystem-info.ps1 @@ -1,4 +1,4 @@ -# subsystem-info v1.0 — Compact summary of 1C subsystem structure +# subsystem-info v1.1 — Compact summary of 1C subsystem structure # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory=$true)][Alias('Path')][string]$SubsystemPath, @@ -17,6 +17,65 @@ $ErrorActionPreference = 'Stop' $script:lines = @() function Out([string]$text) { $script:lines += $text } +function Get-SupportStatusForPath([string]$targetPath) { + try { + $rp = (Resolve-Path $targetPath).Path + $elemUuid = $null + $binPath = $null + # Reads the uuid of the first metadata element in an .xml file (or $null). + function Get-RootUuid([string]$xmlPath) { + if (-not (Test-Path $xmlPath)) { return $null } + try { + [xml]$mx = Get-Content -Path $xmlPath -Encoding UTF8 + $el = $mx.DocumentElement.FirstChild + while ($el -and $el.NodeType -ne 'Element') { $el = $el.NextSibling } + if ($el) { $u = $el.GetAttribute("uuid"); if ($u) { return $u } } + } catch {} + return $null + } + # The target file itself may be the element meta-xml (e.g. Subsystems/X.xml). + $elemUuid = Get-RootUuid $rp + $d = [System.IO.Path]::GetDirectoryName($rp) + for ($i = 0; $i -lt 12 -and $d; $i++) { + if (-not $elemUuid) { $elemUuid = Get-RootUuid "$d.xml" } + if (-not $binPath) { + $cand = Join-Path (Join-Path $d "Ext") "ParentConfigurations.bin" + if ((Test-Path $cand) -or (Test-Path (Join-Path $d "Configuration.xml"))) { $binPath = $cand } + } + if ($elemUuid -and $binPath) { break } + $parent = [System.IO.Path]::GetDirectoryName($d) + if ($parent -eq $d) { break } + $d = $parent + } + if (-not $binPath -or -not (Test-Path $binPath)) { return "не на поддержке" } + $bytes = [System.IO.File]::ReadAllBytes($binPath) + if ($bytes.Length -le 32) { return "снято с поддержки (правки свободны)" } + $start = 0 + if ($bytes.Length -ge 3 -and $bytes[0] -eq 0xEF -and $bytes[1] -eq 0xBB -and $bytes[2] -eq 0xBF) { $start = 3 } + $text = [System.Text.Encoding]::UTF8.GetString($bytes, $start, $bytes.Length - $start) + $h = [regex]::Match($text, '^\{6,(\d+),(\d+),') + if (-not $h.Success) { return "не на поддержке" } + $G = [int]$h.Groups[1].Value + $K = [int]$h.Groups[2].Value + if ($K -eq 0) { return "снято с поддержки (правки свободны)" } + if ($G -eq 1) { return "конфигурация read-only (возможность изменения выключена) — правки невозможны без включения" } + if (-not $elemUuid) { return "не на поддержке" } + $u = [regex]::Escape($elemUuid.ToLower()) + $best = $null + foreach ($m in [regex]::Matches($text, "([0-2]),0,$u")) { + $f1 = [int]$m.Groups[1].Value + if ($null -eq $best -or $f1 -lt $best) { $best = $f1 } + } + if ($null -eq $best) { return "не на поддержке" } + switch ($best) { + 0 { return "на замке — прямая правка сломает обновления; дорабатывай через cfe-* либо включи редактирование объекта" } + 1 { return "редактируется с сохранением поддержки" } + 2 { return "снято с поддержки (правки свободны)" } + } + return "не на поддержке" + } catch { return "не на поддержке" } +} + # --- Resolve path --- if (-not [System.IO.Path]::IsPathRooted($SubsystemPath)) { $SubsystemPath = Join-Path (Get-Location).Path $SubsystemPath @@ -116,6 +175,7 @@ function Get-SubsystemDir([string]$xmlPath) { # --- Show functions for full mode --- function Show-Overview { Out "Подсистема: $subName" + Out "Поддержка: $(Get-SupportStatusForPath $SubsystemPath)" if ($synonym -and $synonym -ne $subName) { Out "Синоним: $synonym" } if ($commentText) { Out "Комментарий: $commentText" } Out "ВключатьВКомандныйИнтерфейс: $inclCI" diff --git a/.claude/skills/subsystem-info/scripts/subsystem-info.py b/.claude/skills/subsystem-info/scripts/subsystem-info.py index 38a8b8e1..73c5935e 100644 --- a/.claude/skills/subsystem-info/scripts/subsystem-info.py +++ b/.claude/skills/subsystem-info/scripts/subsystem-info.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# subsystem-info v1.0 — Compact summary of 1C subsystem structure +# subsystem-info v1.1 — Compact summary of 1C subsystem structure # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -137,9 +137,78 @@ def get_subsystem_dir(xml_path): return os.path.join(dir_name, base_name) # --- Show functions --- +def get_support_status_for_path(target_path): + try: + def root_uuid(xml_path): + if not os.path.isfile(xml_path): + return None + try: + mx = etree.parse(xml_path).getroot() + for child in mx: + if isinstance(child.tag, str) and child.get("uuid"): + return child.get("uuid") + except Exception: + pass + return None + rp = os.path.abspath(target_path) + # The target file itself may be the element meta-xml (e.g. Subsystems/X.xml). + elem_uuid = root_uuid(rp) + bin_path = None + d = os.path.dirname(rp) + for _ in range(12): + if not d: + break + if not elem_uuid: + elem_uuid = root_uuid(d + ".xml") + if not bin_path: + cand = os.path.join(d, "Ext", "ParentConfigurations.bin") + if os.path.exists(cand) or os.path.exists(os.path.join(d, "Configuration.xml")): + bin_path = cand + if elem_uuid and bin_path: + break + parent = os.path.dirname(d) + if parent == d: + break + d = parent + if not bin_path or not os.path.exists(bin_path): + return "не на поддержке" + data = open(bin_path, "rb").read() + if len(data) <= 32: + return "снято с поддержки (правки свободны)" + if data[:3] == b"\xef\xbb\xbf": + data = data[3:] + text = data.decode("utf-8", "replace") + h = re.match(r"\{6,(\d+),(\d+),", text) + if not h: + return "не на поддержке" + g = int(h.group(1)) + k = int(h.group(2)) + if k == 0: + return "снято с поддержки (правки свободны)" + if g == 1: + return "конфигурация read-only (возможность изменения выключена) — правки невозможны без включения" + if not elem_uuid: + return "не на поддержке" + best = None + for m in re.finditer(r"([0-2]),0," + re.escape(elem_uuid.lower()), text): + f1 = int(m.group(1)) + if best is None or f1 < best: + best = f1 + if best is None: + return "не на поддержке" + return { + 0: "на замке — прямая правка сломает обновления; дорабатывай через cfe-* либо включи редактирование объекта", + 1: "редактируется с сохранением поддержки", + 2: "снято с поддержки (правки свободны)", + }.get(best, "не на поддержке") + except Exception: + return "не на поддержке" + + def show_overview(sub_name, synonym, comment_text, incl_ci, use_one_cmd, explanation, pic_text, content_items, groups, child_names, has_ci): out(f"Подсистема: {sub_name}") + out(f"Поддержка: {get_support_status_for_path(subsystem_path)}") if synonym and synonym != sub_name: out(f"Синоним: {synonym}") if comment_text: diff --git a/tests/skills/cases/form-info/real-acc-support.json b/tests/skills/cases/form-info/real-acc-support.json new file mode 100644 index 00000000..8c283a8d --- /dev/null +++ b/tests/skills/cases/form-info/real-acc-support.json @@ -0,0 +1,6 @@ +{ + "name": "Поддержка формы: acc — конфигурация read-only (G=1)", + "setup": "external:C:/WS/tasks/cfsrc/acc_8.3.24", + "params": { "formPath": "Catalogs/Валюты/Forms/ФормаСписка/Ext/Form.xml" }, + "expect": { "stdoutContains": "Поддержка: конфигурация read-only" } +} diff --git a/tests/skills/cases/mxl-info/real-acc-support.json b/tests/skills/cases/mxl-info/real-acc-support.json new file mode 100644 index 00000000..8708611d --- /dev/null +++ b/tests/skills/cases/mxl-info/real-acc-support.json @@ -0,0 +1,6 @@ +{ + "name": "Реальный макет ВводНачальныхОстатков.Справка — поддержка (БП)", + "setup": "external:C:/WS/tasks/cfsrc/acc_8.3.24", + "params": { "templatePath": "DataProcessors/ВводНачальныхОстатков/Templates/Справка/Ext/Template.xml" }, + "expect": { "stdoutContains": "Поддержка: конфигурация read-only" } +} diff --git a/tests/skills/cases/role-info/real-acc-support.json b/tests/skills/cases/role-info/real-acc-support.json new file mode 100644 index 00000000..6b68d225 --- /dev/null +++ b/tests/skills/cases/role-info/real-acc-support.json @@ -0,0 +1,6 @@ +{ + "name": "Состояние поддержки роли (БП, read-only)", + "setup": "external:C:/WS/tasks/cfsrc/acc_8.3.24", + "params": { "rightsPath": "Roles/АдминистраторСистемы/Ext/Rights.xml" }, + "expect": { "stdoutContains": "Поддержка: конфигурация read-only" } +} diff --git a/tests/skills/cases/skd-info/real-acc-support.json b/tests/skills/cases/skd-info/real-acc-support.json new file mode 100644 index 00000000..be052349 --- /dev/null +++ b/tests/skills/cases/skd-info/real-acc-support.json @@ -0,0 +1,6 @@ +{ + "name": "Поддержка: read-only конфигурация (БП)", + "setup": "external:C:/WS/tasks/cfsrc/acc_8.3.24", + "params": { "templatePath": "Reports/АнализВзносовВФонды/Templates/ОсновнаяСхемаКомпоновкиДанных/Ext/Template.xml" }, + "expect": { "stdoutContains": "Поддержка: конфигурация read-only" } +} diff --git a/tests/skills/cases/subsystem-info/real-acc-support.json b/tests/skills/cases/subsystem-info/real-acc-support.json new file mode 100644 index 00000000..363c4fec --- /dev/null +++ b/tests/skills/cases/subsystem-info/real-acc-support.json @@ -0,0 +1,6 @@ +{ + "name": "Поддержка: read-only конфигурация (БП)", + "setup": "external:C:/WS/tasks/cfsrc/acc_8.3.24", + "params": { "subsystemPath": "Subsystems/Администрирование.xml" }, + "expect": { "stdoutContains": "Поддержка: конфигурация read-only" } +}