mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-06-13 17:34:57 +03:00
Add /role-info and /role-compile skills
role-info: PS1 script parsing Rights.xml into compact summary grouped by object type. Supports -ShowDenied and -OutFile for UTF-8 output. 78K lines XML -> 1924 lines for largest role, ~100 for typical ones. role-compile: Template-based SKILL.md (no script) with XML templates, rights catalog per object type, and typical right sets. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,209 @@
|
||||
---
|
||||
name: role-compile
|
||||
description: Создание роли 1С — метаданные и Rights.xml из описания прав
|
||||
argument-hint: <RoleName> <RolesDir>
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
- Write
|
||||
- Glob
|
||||
---
|
||||
|
||||
# /role-compile — создание роли 1С
|
||||
|
||||
Создаёт файлы роли (метаданные + Rights.xml) по описанию прав. Скрипта нет — агент генерирует XML по шаблонам ниже.
|
||||
|
||||
## Использование
|
||||
|
||||
```
|
||||
/role-compile <RoleName> <RolesDir>
|
||||
```
|
||||
|
||||
- **RoleName** — программное имя роли (например, `ВыполнениеРегламентныхЗаданий`)
|
||||
- **RolesDir** — каталог `Roles/` в исходниках конфигурации или обработки
|
||||
|
||||
## Что создать
|
||||
|
||||
### 1. Файл метаданных: `Roles/<RoleName>.xml`
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:xr="http://v8.1c.ru/8.3/xcf/readable"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
version="2.17">
|
||||
<Role uuid="GENERATE-UUID-HERE">
|
||||
<Properties>
|
||||
<Name>ИмяРоли</Name>
|
||||
<Synonym>
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Отображаемое имя роли</v8:content>
|
||||
</v8:item>
|
||||
</Synonym>
|
||||
<Comment/>
|
||||
</Properties>
|
||||
</Role>
|
||||
</MetaDataObject>
|
||||
```
|
||||
|
||||
**UUID:** Сгенерируй через PowerShell: `[guid]::NewGuid().ToString()`
|
||||
|
||||
**Namespace:** Минимальный набор — достаточно `xmlns`, `v8`, `xr`, `xs`, `xsi`. Полный набор из спецификации тоже корректен.
|
||||
|
||||
### 2. Файл прав: `Roles/<RoleName>/Ext/Rights.xml`
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Rights xmlns="http://v8.1c.ru/8.2/roles"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:type="Rights" version="2.17">
|
||||
<setForNewObjects>false</setForNewObjects>
|
||||
<setForAttributesByDefault>true</setForAttributesByDefault>
|
||||
<independentRightsOfChildObjects>false</independentRightsOfChildObjects>
|
||||
<!-- объекты с правами -->
|
||||
</Rights>
|
||||
```
|
||||
|
||||
### 3. Регистрация в Configuration.xml
|
||||
|
||||
Добавь `<Role>ИмяРоли</Role>` в секцию `<ChildObjects>` файла `Configuration.xml`.
|
||||
|
||||
## Формат блока прав
|
||||
|
||||
Каждый объект — отдельный блок `<object>`:
|
||||
|
||||
```xml
|
||||
<object>
|
||||
<name>ТипОбъекта.ИмяОбъекта</name>
|
||||
<right>
|
||||
<name>ИмяПрава</name>
|
||||
<value>true</value>
|
||||
</right>
|
||||
</object>
|
||||
```
|
||||
|
||||
Несколько прав — несколько `<right>` внутри одного `<object>`.
|
||||
|
||||
## Права по типам объектов (краткая справка)
|
||||
|
||||
### Ссылочные объекты данных
|
||||
|
||||
| Тип | Типичные права |
|
||||
|-----|---------------|
|
||||
| `Catalog` | Read, Insert, Update, Delete, View, Edit, InputByString, InteractiveInsert, InteractiveSetDeletionMark, InteractiveClearDeletionMark, InteractiveDelete |
|
||||
| `Document` | (все Catalog) + Posting, UndoPosting, InteractivePosting, InteractivePostingRegular, InteractiveUndoPosting, InteractiveChangeOfPosted |
|
||||
| `ChartOfAccounts` | (как Catalog) + предопределённые: InteractiveDeletePredefinedData и др. |
|
||||
| `ChartOfCharacteristicTypes` | (как ChartOfAccounts) |
|
||||
| `ChartOfCalculationTypes` | (как ChartOfAccounts) |
|
||||
| `ExchangePlan` | (как Catalog) |
|
||||
| `BusinessProcess` | (как Catalog) + Start, InteractiveStart, InteractiveActivate |
|
||||
| `Task` | (как Catalog) + Execute, InteractiveExecute, InteractiveActivate |
|
||||
|
||||
### Регистры
|
||||
|
||||
| Тип | Права |
|
||||
|-----|-------|
|
||||
| `InformationRegister` | Read, Update, View, Edit, TotalsControl |
|
||||
| `AccumulationRegister` | Read, Update, View, Edit, TotalsControl |
|
||||
| `AccountingRegister` | Read, Update, View, Edit, TotalsControl |
|
||||
| `CalculationRegister` | Read, View |
|
||||
|
||||
### Простые типы
|
||||
|
||||
| Тип | Права |
|
||||
|-----|-------|
|
||||
| `DataProcessor` | Use, View |
|
||||
| `Report` | Use, View |
|
||||
| `Constant` | Read, Update, View, Edit |
|
||||
| `CommonForm` | View |
|
||||
| `CommonCommand` | View |
|
||||
| `Subsystem` | View |
|
||||
| `DocumentJournal` | Read, View |
|
||||
| `Sequence` | Read, Update |
|
||||
| `SessionParameter` | Get, Set |
|
||||
| `CommonAttribute` | View, Edit |
|
||||
| `WebService` / `HTTPService` / `IntegrationService` | Use |
|
||||
|
||||
### Вложенные объекты
|
||||
|
||||
| Вложенный тип | Права | Пример |
|
||||
|--------------|-------|--------|
|
||||
| `*.StandardAttribute.*` | View, Edit | `Document.Реализация.StandardAttribute.Posted` |
|
||||
| `*.Attribute.*` | View, Edit | `Catalog.Контрагенты.Attribute.ИНН` |
|
||||
| `*.TabularSection.*` | View, Edit | `Document.Реализация.TabularSection.Товары` |
|
||||
| `*Register.*.Dimension.*` | View, Edit | `InformationRegister.Цены.Dimension.Номенклатура` |
|
||||
| `*Register.*.Resource.*` | View, Edit | `InformationRegister.Цены.Resource.Цена` |
|
||||
| `*.Command.*` | View | `Catalog.Контрагенты.Command.Открыть` |
|
||||
|
||||
### Configuration
|
||||
|
||||
Права на конфигурацию в целом: `Configuration.ИмяКонфигурации`
|
||||
|
||||
Ключевые: Administration, DataAdministration, ThinClient, WebClient, ThickClient, ExternalConnection, Output, SaveUserData, InteractiveOpenExtDataProcessors, InteractiveOpenExtReports
|
||||
|
||||
## Типичные наборы прав
|
||||
|
||||
### Чтение справочника
|
||||
|
||||
```xml
|
||||
<object>
|
||||
<name>Catalog.Номенклатура</name>
|
||||
<right><name>Read</name><value>true</value></right>
|
||||
<right><name>View</name><value>true</value></right>
|
||||
<right><name>InputByString</name><value>true</value></right>
|
||||
</object>
|
||||
```
|
||||
|
||||
### Полные права на документ
|
||||
|
||||
```xml
|
||||
<object>
|
||||
<name>Document.РеализацияТоваровУслуг</name>
|
||||
<right><name>Read</name><value>true</value></right>
|
||||
<right><name>Insert</name><value>true</value></right>
|
||||
<right><name>Update</name><value>true</value></right>
|
||||
<right><name>Delete</name><value>true</value></right>
|
||||
<right><name>Posting</name><value>true</value></right>
|
||||
<right><name>UndoPosting</name><value>true</value></right>
|
||||
<right><name>View</name><value>true</value></right>
|
||||
<right><name>InteractiveInsert</name><value>true</value></right>
|
||||
<right><name>Edit</name><value>true</value></right>
|
||||
<right><name>InteractiveSetDeletionMark</name><value>true</value></right>
|
||||
<right><name>InteractiveClearDeletionMark</name><value>true</value></right>
|
||||
<right><name>InteractivePosting</name><value>true</value></right>
|
||||
<right><name>InteractivePostingRegular</name><value>true</value></right>
|
||||
<right><name>InteractiveUndoPosting</name><value>true</value></right>
|
||||
<right><name>InteractiveChangeOfPosted</name><value>true</value></right>
|
||||
<right><name>InputByString</name><value>true</value></right>
|
||||
</object>
|
||||
```
|
||||
|
||||
### Использование обработки
|
||||
|
||||
```xml
|
||||
<object>
|
||||
<name>DataProcessor.ОбновлениеЦен</name>
|
||||
<right><name>Use</name><value>true</value></right>
|
||||
<right><name>View</name><value>true</value></right>
|
||||
</object>
|
||||
```
|
||||
|
||||
### Чтение/запись регистра
|
||||
|
||||
```xml
|
||||
<object>
|
||||
<name>InformationRegister.ЦеныНоменклатуры</name>
|
||||
<right><name>Read</name><value>true</value></right>
|
||||
<right><name>Update</name><value>true</value></right>
|
||||
<right><name>View</name><value>true</value></right>
|
||||
<right><name>Edit</name><value>true</value></right>
|
||||
</object>
|
||||
```
|
||||
|
||||
## Полная спецификация
|
||||
|
||||
См. [1c-role-spec.md](../../docs/1c-role-spec.md) — полный каталог прав, RLS, шаблоны ограничений, версии формата.
|
||||
@@ -0,0 +1,74 @@
|
||||
---
|
||||
name: role-info
|
||||
description: Компактная сводка прав роли 1С из Rights.xml — объекты, права, RLS, шаблоны ограничений
|
||||
argument-hint: <RightsPath>
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
---
|
||||
|
||||
# /role-info — анализ роли 1С
|
||||
|
||||
Парсит `Rights.xml` роли и выдаёт компактную сводку: объекты сгруппированы по типу, показаны только разрешённые права. Сжатие: тысячи строк XML → 50–150 строк текста.
|
||||
|
||||
## Использование
|
||||
|
||||
```
|
||||
/role-info <RightsPath>
|
||||
```
|
||||
|
||||
**RightsPath** — путь к файлу `Rights.xml` роли (обычно `Roles/ИмяРоли/Ext/Rights.xml`).
|
||||
|
||||
## Запуск скрипта
|
||||
|
||||
```powershell
|
||||
powershell.exe -File .claude\skills\role-info\scripts\role-info.ps1 -RightsPath <path> -OutFile <output.txt>
|
||||
```
|
||||
|
||||
### Параметры
|
||||
|
||||
| Параметр | Обязательный | Описание |
|
||||
|----------|:------------:|----------|
|
||||
| `-RightsPath` | да | Путь к Rights.xml |
|
||||
| `-ShowDenied` | нет | Показать запрещённые права (по умолчанию скрыты) |
|
||||
| `-MaxPerGroup` | нет | Макс. объектов на группу (по умолчанию 20). `0` = без ограничений |
|
||||
| `-OutFile` | нет | Записать результат в файл (UTF-8 BOM). Без этого — вывод в консоль |
|
||||
|
||||
**Важно:** Всегда используй `-OutFile` и читай результат через Read tool. Прямой вывод в консоль через bash ломает кириллицу.
|
||||
|
||||
## Формат вывода
|
||||
|
||||
```
|
||||
=== Role: БазовыеПраваБП --- "Базовые права: Бухгалтерия предприятия" ===
|
||||
|
||||
Properties: setForNewObjects=false, setForAttributesByDefault=true, independentRightsOfChildObjects=false
|
||||
|
||||
Allowed rights:
|
||||
|
||||
Catalog (8):
|
||||
Контрагенты: Read, View, InputByString
|
||||
Банки: Read, View, InputByString
|
||||
...
|
||||
|
||||
Document (12):
|
||||
РеализацияТоваровУслуг: Read, View, Posting, InteractivePosting
|
||||
...
|
||||
|
||||
InformationRegister (6):
|
||||
ЦеныНоменклатуры: Read [RLS], Update
|
||||
...
|
||||
|
||||
Denied: 18 rights (use -ShowDenied to list)
|
||||
|
||||
RLS: 4 restrictions
|
||||
Templates: ДляРегистра, ПоЗначениям
|
||||
|
||||
---
|
||||
Total: 138 allowed, 18 denied
|
||||
```
|
||||
|
||||
### Обозначения
|
||||
|
||||
- `[RLS]` — право с ограничением на уровне записей (restrictionByCondition)
|
||||
- `-View`, `-Edit` — запрещённые права (в секции Denied, при `-ShowDenied`)
|
||||
- Вложенные объекты показываются с суффиксом: `Контрагенты.StandardAttribute.PredefinedDataName`
|
||||
@@ -0,0 +1,231 @@
|
||||
param(
|
||||
[Parameter(Mandatory=$true)][string]$RightsPath,
|
||||
[switch]$ShowDenied,
|
||||
[int]$MaxPerGroup = 20,
|
||||
[string]$OutFile
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
# --- Output helper ---
|
||||
$script:lines = @()
|
||||
function Out([string]$text) {
|
||||
if ($OutFile) { $script:lines += $text }
|
||||
else { Write-Host $text }
|
||||
}
|
||||
|
||||
# --- Resolve paths ---
|
||||
if (-not [System.IO.Path]::IsPathRooted($RightsPath)) {
|
||||
$RightsPath = Join-Path (Get-Location).Path $RightsPath
|
||||
}
|
||||
|
||||
if (-not (Test-Path $RightsPath)) {
|
||||
Out "[ERROR] File not found: $RightsPath"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# --- Try to find metadata file for role name/synonym ---
|
||||
$roleName = ""
|
||||
$roleSynonym = ""
|
||||
$extDir = Split-Path $RightsPath # .../Ext
|
||||
$roleDir = Split-Path $extDir # .../RoleName
|
||||
$rolesDir = Split-Path $roleDir # .../Roles
|
||||
$roleFolderName = Split-Path $roleDir -Leaf
|
||||
$metaPath = Join-Path $rolesDir "$roleFolderName.xml"
|
||||
|
||||
if (Test-Path $metaPath) {
|
||||
try {
|
||||
[xml]$metaXml = Get-Content -Path $metaPath -Encoding UTF8
|
||||
$ns = New-Object System.Xml.XmlNamespaceManager($metaXml.NameTable)
|
||||
$ns.AddNamespace("md", "http://v8.1c.ru/8.3/MDClasses")
|
||||
$ns.AddNamespace("v8", "http://v8.1c.ru/8.1/data/core")
|
||||
$nameNode = $metaXml.SelectSingleNode("//md:Role/md:Properties/md:Name", $ns)
|
||||
if ($nameNode) { $roleName = $nameNode.InnerText }
|
||||
$synNode = $metaXml.SelectSingleNode("//md:Role/md:Properties/md:Synonym/v8:item[v8:lang='ru']/v8:content", $ns)
|
||||
if ($synNode) { $roleSynonym = $synNode.InnerText }
|
||||
} catch {
|
||||
# Ignore metadata parsing errors
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $roleName) { $roleName = $roleFolderName }
|
||||
|
||||
# --- Parse Rights.xml ---
|
||||
[xml]$xml = Get-Content -Path $RightsPath -Encoding UTF8
|
||||
$root = $xml.DocumentElement
|
||||
$rightsNs = "http://v8.1c.ru/8.2/roles"
|
||||
|
||||
# Global flags
|
||||
$setForNew = $root.setForNewObjects
|
||||
$setForAttrs = $root.setForAttributesByDefault
|
||||
$independentChild = $root.independentRightsOfChildObjects
|
||||
|
||||
# --- Collect objects ---
|
||||
# Structure: grouped by type prefix, then by object short name
|
||||
$allowed = [ordered]@{} # type -> [ordered]@{ shortName -> [list of rights] }
|
||||
$denied = [ordered]@{} # type -> [ordered]@{ shortName -> [list of rights] }
|
||||
$rlsObjects = @()
|
||||
$totalAllowed = 0
|
||||
$totalDenied = 0
|
||||
|
||||
$objects = $root.GetElementsByTagName("object", $rightsNs)
|
||||
foreach ($obj in $objects) {
|
||||
$objName = ""
|
||||
$rights = @()
|
||||
|
||||
foreach ($child in $obj.ChildNodes) {
|
||||
if ($child.LocalName -eq "name" -and $child.NamespaceURI -eq $rightsNs) {
|
||||
$objName = $child.InnerText
|
||||
}
|
||||
if ($child.LocalName -eq "right" -and $child.NamespaceURI -eq $rightsNs) {
|
||||
$rName = ""
|
||||
$rValue = ""
|
||||
$hasRLS = $false
|
||||
foreach ($rc in $child.ChildNodes) {
|
||||
if ($rc.LocalName -eq "name") { $rName = $rc.InnerText }
|
||||
if ($rc.LocalName -eq "value") { $rValue = $rc.InnerText }
|
||||
if ($rc.LocalName -eq "restrictionByCondition") { $hasRLS = $true }
|
||||
}
|
||||
if ($rName -and $rValue) {
|
||||
$rights += @{ name = $rName; value = $rValue; rls = $hasRLS }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $objName -or $rights.Count -eq 0) { continue }
|
||||
|
||||
# Split into type prefix and short name
|
||||
$dotIdx = $objName.IndexOf(".")
|
||||
if ($dotIdx -lt 0) { continue }
|
||||
$typePrefix = $objName.Substring(0, $dotIdx)
|
||||
$shortName = $objName.Substring($dotIdx + 1)
|
||||
|
||||
foreach ($r in $rights) {
|
||||
if ($r.value -eq "true") {
|
||||
$totalAllowed++
|
||||
if (-not $allowed.Contains($typePrefix)) {
|
||||
$allowed[$typePrefix] = [ordered]@{}
|
||||
}
|
||||
if (-not $allowed[$typePrefix].Contains($shortName)) {
|
||||
$allowed[$typePrefix][$shortName] = @()
|
||||
}
|
||||
$suffix = $r.name
|
||||
if ($r.rls) {
|
||||
$suffix += " [RLS]"
|
||||
$rlsObjects += "$typePrefix.$shortName ($($r.name))"
|
||||
}
|
||||
$allowed[$typePrefix][$shortName] += $suffix
|
||||
}
|
||||
else {
|
||||
$totalDenied++
|
||||
if (-not $denied.Contains($typePrefix)) {
|
||||
$denied[$typePrefix] = [ordered]@{}
|
||||
}
|
||||
if (-not $denied[$typePrefix].Contains($shortName)) {
|
||||
$denied[$typePrefix][$shortName] = @()
|
||||
}
|
||||
$denied[$typePrefix][$shortName] += $r.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# --- Restriction templates ---
|
||||
$templates = @()
|
||||
$tplNodes = $root.GetElementsByTagName("restrictionTemplate", $rightsNs)
|
||||
foreach ($tpl in $tplNodes) {
|
||||
foreach ($child in $tpl.ChildNodes) {
|
||||
if ($child.LocalName -eq "name") {
|
||||
$tName = $child.InnerText
|
||||
# Extract just the name part before parentheses
|
||||
$parenIdx = $tName.IndexOf("(")
|
||||
if ($parenIdx -gt 0) { $tName = $tName.Substring(0, $parenIdx) }
|
||||
$templates += $tName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# --- Output ---
|
||||
$header = "=== Role: $roleName"
|
||||
if ($roleSynonym) { $header += " --- `"$roleSynonym`"" }
|
||||
$header += " ==="
|
||||
Out $header
|
||||
Out ""
|
||||
|
||||
Out "Properties: setForNewObjects=$setForNew, setForAttributesByDefault=$setForAttrs, independentRightsOfChildObjects=$independentChild"
|
||||
Out ""
|
||||
|
||||
# Helper: output group with truncation
|
||||
function OutGroup($objMap, [string]$prefix, [switch]$isDenied) {
|
||||
$keys = @($objMap.Keys)
|
||||
$shown = 0
|
||||
foreach ($shortName in $keys) {
|
||||
if ($MaxPerGroup -gt 0 -and $shown -ge $MaxPerGroup) {
|
||||
$remaining = $keys.Count - $shown
|
||||
Out " ... ещё $remaining (используй -MaxPerGroup 0 для полного списка)"
|
||||
break
|
||||
}
|
||||
if ($isDenied) {
|
||||
$rightsList = ($objMap[$shortName] | ForEach-Object { "-$_" }) -join ", "
|
||||
} else {
|
||||
$rightsList = $objMap[$shortName] -join ", "
|
||||
}
|
||||
Out " ${shortName}: $rightsList"
|
||||
$shown++
|
||||
}
|
||||
}
|
||||
|
||||
# Allowed rights grouped by type
|
||||
if ($allowed.Count -gt 0) {
|
||||
Out "Allowed rights:"
|
||||
Out ""
|
||||
foreach ($typePrefix in $allowed.Keys) {
|
||||
$objMap = $allowed[$typePrefix]
|
||||
Out " $typePrefix ($($objMap.Count)):"
|
||||
OutGroup $objMap $typePrefix
|
||||
Out ""
|
||||
}
|
||||
}
|
||||
else {
|
||||
Out "(no allowed rights)"
|
||||
Out ""
|
||||
}
|
||||
|
||||
# Denied rights
|
||||
if ($ShowDenied -and $denied.Count -gt 0) {
|
||||
Out "Denied rights:"
|
||||
Out ""
|
||||
foreach ($typePrefix in $denied.Keys) {
|
||||
$objMap = $denied[$typePrefix]
|
||||
Out " $typePrefix ($($objMap.Count)):"
|
||||
OutGroup $objMap $typePrefix -isDenied
|
||||
Out ""
|
||||
}
|
||||
}
|
||||
elseif ($totalDenied -gt 0) {
|
||||
Out "Denied: $totalDenied rights (use -ShowDenied to list)"
|
||||
Out ""
|
||||
}
|
||||
|
||||
# RLS summary
|
||||
if ($rlsObjects.Count -gt 0) {
|
||||
Out "RLS: $($rlsObjects.Count) restrictions"
|
||||
}
|
||||
|
||||
# Templates
|
||||
if ($templates.Count -gt 0) {
|
||||
Out "Templates: $($templates -join ', ')"
|
||||
}
|
||||
|
||||
Out ""
|
||||
Out "---"
|
||||
Out "Total: $totalAllowed allowed, $totalDenied denied"
|
||||
|
||||
# --- Write to file if requested ---
|
||||
if ($OutFile) {
|
||||
if (-not [System.IO.Path]::IsPathRooted($OutFile)) {
|
||||
$OutFile = Join-Path (Get-Location).Path $OutFile
|
||||
}
|
||||
$utf8 = New-Object System.Text.UTF8Encoding($true)
|
||||
[System.IO.File]::WriteAllLines($OutFile, $script:lines, $utf8)
|
||||
Write-Host "Output written to $OutFile"
|
||||
}
|
||||
Reference in New Issue
Block a user