feat(tools): раскладка навыков junction'ами из plugin/skills

This commit is contained in:
2026-07-31 01:36:42 +03:00
parent 4a6dbd1497
commit 97e95d5f44
5 changed files with 182 additions and 0 deletions
+11
View File
@@ -53,6 +53,9 @@ Desktop.ini
# ---- Scratch: тестовые документы КОМПАС, снимки рендера --------------------
.scratch/
# ---- pi extension node_modules (не коммитить, source остаётся) ----
.pi/extensions/*/node_modules/
# ---- Use case'ы проработки MCP (локальная песочница, не разделяется) --------
# Папки кейсов с описаниями (case.md) и артефактами построений (.m3d, .png …).
# Каркас и правила — в usecases/README.md (см. там же шаблон _TEMPLATE/).
@@ -67,6 +70,14 @@ usecases/
# коммитим: если будет скачано локально снова — пусть остаётся проигнорированным.
docs/KOMPAS_SDK_ru-RU/
# ---- Навыки: источник истины — plugin/skills, остальное создаётся junction'ами -----
# tools/sync-agent-assets.ps1. Пути точечные: git видит junction как обычный каталог
# и без этого проиндексировал бы содержимое повторно.
/.claude/skills/kompas-3d/
/.claude/skills/kompas-fdm-design/
/.agents/skills/kompas-3d/
/.agents/skills/kompas-fdm-design/
# ============================================================================
# Отслеживается НАМЕРЕННО (нужно для сборки), не игнорировать:
# libs/kompas-interop/*.dll — interop-сборки КОМПАС из SDK (Samples/Common)
+68
View File
@@ -0,0 +1,68 @@
Set-StrictMode -Version Latest
<#
.SYNOPSIS
Проверяет, что LinkPath — junction, указывающий на SourcePath.
#>
function Test-AgentSkillLink {
[CmdletBinding()]
param(
[Parameter(Mandatory)] [string] $SourcePath,
[Parameter(Mandatory)] [string] $LinkPath
)
$item = Get-Item -LiteralPath $LinkPath -Force -ErrorAction SilentlyContinue
if ($null -eq $item -or $item.LinkType -ne 'Junction') { return $false }
$actual = [System.IO.Path]::GetFullPath(($item.Target | Select-Object -First 1))
return $actual.TrimEnd('\') -eq ([System.IO.Path]::GetFullPath($SourcePath)).TrimEnd('\')
}
<#
.SYNOPSIS
Раскладывает навык по LinkPath junction'ом. Возвращает Created | AlreadyLinked | Copied.
Настоящий каталог никогда не удаляется автоматически.
#>
function Sync-AgentSkillLink {
[CmdletBinding()]
param(
[Parameter(Mandatory)] [string] $SourcePath,
[Parameter(Mandatory)] [string] $LinkPath,
[switch] $AllowCopy
)
if (-not (Test-Path -LiteralPath $SourcePath)) {
throw "нет исходного навыка: $SourcePath"
}
if (Test-AgentSkillLink -SourcePath $SourcePath -LinkPath $LinkPath) {
return 'AlreadyLinked'
}
$existing = Get-Item -LiteralPath $LinkPath -Force -ErrorAction SilentlyContinue
if ($null -ne $existing) {
if ($existing.LinkType -ne 'Junction') {
throw "$LinkPath существует и не является junction — удалите его вручную и повторите"
}
# Ссылка ведёт не туда: удаляем сам reparse point, не заходя в target.
[System.IO.Directory]::Delete($LinkPath)
}
$parent = Split-Path -Parent $LinkPath
if (-not (Test-Path -LiteralPath $parent)) {
New-Item -ItemType Directory -Path $parent -Force | Out-Null
}
try {
New-Item -ItemType Junction -Path $LinkPath -Value $SourcePath -ErrorAction Stop | Out-Null
return 'Created'
}
catch {
if (-not $AllowCopy) {
throw "не удалось создать junction $LinkPath -> $SourcePath ($($_.Exception.Message)). Повторите с -AllowCopy, если копия допустима."
}
Copy-Item -LiteralPath $SourcePath -Destination $LinkPath -Recurse -Force
return 'Copied'
}
}
Export-ModuleMember -Function Test-AgentSkillLink, Sync-AgentSkillLink
+42
View File
@@ -0,0 +1,42 @@
#requires -Version 5.1
[CmdletBinding()]
param(
[switch] $Check,
[switch] $AllowCopy
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
Import-Module (Join-Path $PSScriptRoot 'AgentAssets.psm1') -Force
$repoRoot = Split-Path -Parent $PSScriptRoot
$skills = @('kompas-3d', 'kompas-fdm-design')
$targetRoots = @(
(Join-Path $repoRoot '.claude\skills'),
(Join-Path $repoRoot '.agents\skills')
)
$failed = $false
foreach ($root in $targetRoots) {
foreach ($skill in $skills) {
$source = Join-Path $repoRoot "plugin\skills\$skill"
$link = Join-Path $root $skill
if ($Check) {
if (Test-AgentSkillLink -SourcePath $source -LinkPath $link) {
Write-Host "ok $link"
}
else {
Write-Host "БИТО $link"
$failed = $true
}
continue
}
$state = Sync-AgentSkillLink -SourcePath $source -LinkPath $link -AllowCopy:$AllowCopy
Write-Host "$state $link"
}
}
if ($failed) { exit 1 }
+50
View File
@@ -0,0 +1,50 @@
BeforeAll {
Import-Module (Join-Path $PSScriptRoot '..' 'AgentAssets.psm1') -Force
function New-TempTree {
$root = Join-Path ([System.IO.Path]::GetTempPath()) ("agentassets_" + [guid]::NewGuid().ToString('N'))
$source = Join-Path $root 'plugin/skills/demo'
New-Item -ItemType Directory -Path $source -Force | Out-Null
Set-Content -LiteralPath (Join-Path $source 'SKILL.md') -Value 'demo' -Encoding utf8
return [pscustomobject]@{ Root = $root; Source = $source; Link = (Join-Path $root '.claude/skills/demo') }
}
}
Describe 'Sync-AgentSkillLink' {
It 'создаёт junction на исходный навык' {
$t = New-TempTree
try {
Sync-AgentSkillLink -SourcePath $t.Source -LinkPath $t.Link | Should -Be 'Created'
(Get-Item -LiteralPath $t.Link -Force).LinkType | Should -Be 'Junction'
Get-Content -LiteralPath (Join-Path $t.Link 'SKILL.md') | Should -Be 'demo'
} finally { Remove-Item -LiteralPath $t.Root -Recurse -Force -ErrorAction SilentlyContinue }
}
It 'идемпотентен: повторный запуск ничего не ломает' {
$t = New-TempTree
try {
Sync-AgentSkillLink -SourcePath $t.Source -LinkPath $t.Link | Out-Null
Sync-AgentSkillLink -SourcePath $t.Source -LinkPath $t.Link | Should -Be 'AlreadyLinked'
} finally { Remove-Item -LiteralPath $t.Root -Recurse -Force -ErrorAction SilentlyContinue }
}
It 'не трогает настоящий каталог, а падает с понятной ошибкой' {
$t = New-TempTree
try {
New-Item -ItemType Directory -Path $t.Link -Force | Out-Null
Set-Content -LiteralPath (Join-Path $t.Link 'mine.md') -Value 'ручное' -Encoding utf8
{ Sync-AgentSkillLink -SourcePath $t.Source -LinkPath $t.Link } | Should -Throw '*не является junction*'
Get-Content -LiteralPath (Join-Path $t.Link 'mine.md') | Should -Be 'ручное'
} finally { Remove-Item -LiteralPath $t.Root -Recurse -Force -ErrorAction SilentlyContinue }
}
It 'Test-AgentSkillLink различает связанное и несвязанное состояние' {
$t = New-TempTree
try {
Test-AgentSkillLink -SourcePath $t.Source -LinkPath $t.Link | Should -BeFalse
Sync-AgentSkillLink -SourcePath $t.Source -LinkPath $t.Link | Out-Null
Test-AgentSkillLink -SourcePath $t.Source -LinkPath $t.Link | Should -BeTrue
} finally { Remove-Item -LiteralPath $t.Root -Recurse -Force -ErrorAction SilentlyContinue }
}
}
+11
View File
@@ -0,0 +1,11 @@
#requires -Version 7.0
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
Import-Module Pester -MinimumVersion 5.5.0
$config = New-PesterConfiguration
$config.Run.Path = $PSScriptRoot
$config.Run.Exit = $true
$config.Output.Verbosity = 'Detailed'
Invoke-Pester -Configuration $config