Files
StudioLift/scripts/install-skills.ps1
Sidney Zhang 9020752eed feat(scripts): 添加 PEP 723 脚本元数据并修复 HTTP/2 伪头过滤问题
- 为 build_studio_urls.py 和 lookup_groups.py 添加 PEP 723 依赖声明
- 修复 HTTP/2 伪头导致 requests 抛 InvalidHeader 的问题
- 添加 CDP 端口连通性预检查及启动指引
2026-08-27 13:33:34 +08:00

212 lines
8.5 KiB
PowerShell
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# =====================================================================
# 技能安装器Windows多 agent 目标)
# 作用:把本项目 skills\ 下全部技能安装到指定的用户级技能目录:
# trae-cn -> %USERPROFILE%\.trae-cn\skills\ Trae CN / Trae SOLO
# zcode -> %USERPROFILE%\.zcode\skills\ ZCode CLI
# 默认安装全部技能到全部已知目录;也可用 -Agent、-Skills 只装一部分,
# 或用 -DryRun 预览。
# 用法:双击同目录下的 install-skills.bat推荐
# powershell -NoProfile -ExecutionPolicy Bypass -File install-skills.ps1
# powershell ... -Agent zcode -Skills yt-studio-url-builder,youtube-studio-csv-download
# powershell ... -Agent all -DryRun
# =====================================================================
param(
# 目标 agenttrae-cn默认/ zcode / all。all = 装到下面全部已知目标。
[string]$Agent = "trae-cn",
# 只安装这些技能(逗号/分号/空格分隔,匹配技能文件夹名,不区分大小写)。缺省=全部。
[string[]]$Skills,
# 只预览将要安装/备份/跳过的技能,不实际复制。
[switch]$DryRun
)
$ErrorActionPreference = "Stop"
# --- 已知目标agent 名 -> 用户级技能目录 -----------------------------------
# 新增 agent 支持时在此登记即可,其余逻辑不变。
$AgentTargets = @{
"trae-cn" = ".trae-cn\skills"
"zcode" = ".zcode\skills"
}
# --- 1. 解析目标目录(可能多个) ---
if ($AgentTargets.ContainsKey($Agent)) {
$destRelList = @($AgentTargets[$Agent])
} elseif ($Agent -eq "all") {
$destRelList = @($AgentTargets.Values)
} else {
Write-Host "[错误] 未知 agent: $Agent(可选: $($AgentTargets.Keys -join ', ') | all" -ForegroundColor Red
exit 1
}
$ScriptDir = $PSScriptRoot
$ProjectRoot = Split-Path -Parent $ScriptDir
$SkillsSource = Join-Path $ProjectRoot "skills"
Write-Host "=== 技能安装器 ===" -ForegroundColor Cyan
Write-Host "技能源目录: $SkillsSource"
Write-Host ("安装目标: " + (($destRelList | ForEach-Object { "$env:USERPROFILE\$_" }) -join ", "))
# 把 -Skills 参数拆成规范化名字列表(兼容逗号/分号/空格分隔)
$skillFilter = @()
if ($Skills) {
$skillFilter = @(
$Skills | ForEach-Object { $_ -split '[,;]' } | ForEach-Object { $_.Trim() } |
Where-Object { $_ }
)
}
# --- 2. 校验源目录并收集技能(含 SKILL.md 的子目录才算) ---
if (-not (Test-Path $SkillsSource)) {
Write-Host "[错误] 找不到技能源目录: $SkillsSource" -ForegroundColor Red
exit 1
}
$allSkills = @(Get-ChildItem -Path $SkillsSource -Directory | Where-Object {
Test-Path (Join-Path $_.FullName "SKILL.md")
})
if ($allSkills.Count -eq 0) {
Write-Host "[错误] 源目录下没有可用技能(每个技能文件夹需包含 SKILL.md" -ForegroundColor Red
exit 1
}
# 可选:漏了 SKILL.md 的目录会被跳过;给个提示,避免以为是 bug
$skippedDirs = @(Get-ChildItem -Path $SkillsSource -Directory | Where-Object {
-not (Test-Path (Join-Path $_.FullName "SKILL.md"))
})
foreach ($d in $skippedDirs) {
Write-Host "[跳过] $($d.Name)(缺 SKILL.md不作为技能" -ForegroundColor DarkGray
}
# 按 -Skills 过滤
$targets = if ($skillFilter.Count -gt 0) {
$wanted = @($skillFilter | ForEach-Object { $_.ToLowerInvariant() })
@($allSkills | Where-Object { $wanted -contains $_.Name.ToLowerInvariant() })
} else {
$allSkills
}
# 过滤后校验:用户指定的技能名是否存在
if ($skillFilter.Count -gt 0 -and $targets.Count -lt $skillFilter.Count) {
$found = @($targets | ForEach-Object { $_.Name.ToLowerInvariant() })
$missing = @($skillFilter | Where-Object { $found -notcontains $_.ToLowerInvariant() })
Write-Host ("[警告] 未找到这些技能: {0}" -f ($missing -join ", ")) -ForegroundColor Yellow
}
if ($targets.Count -eq 0) {
Write-Host "[错误] 没有待安装的技能(-Skills 指定的技能不存在,或源目录无可用技能)。" -ForegroundColor Red
exit 1
}
# --- 3. 读取每个技能的 frontmatter name并校验其与文件夹名一致 ---
# 预测性关键:各 agent 以 frontmatter 的 name 作为技能名,若与文件夹名不一致会引发歧义/旧名残留。
$warnCount = 0
foreach ($skill in $targets) {
$skillFile = Join-Path $skill.FullName "SKILL.md"
$fmName = $null
try {
$lines = Get-Content -Path $skillFile -TotalCount 6 -Encoding UTF8
foreach ($l in $lines) {
if ($l -match '^\s*name\s*[:=]\s*["'']([^"'']+)["'']') {
$fmName = $Matches[1]
break
}
}
} catch {
# 读取失败不阻塞安装
}
if ($fmName -and $fmName -ne $skill.Name) {
$warnCount++
Write-Host ("[警告] {0}: frontmatter name 为 {1},与文件夹名不一致。" -f $skill.Name, $fmName) -ForegroundColor Yellow
} elseif (-not $fmName) {
$warnCount++
Write-Host ("[警告] {0}: SKILL.md 顶部未解析到 name安装后可能无法触发。" -f $skill.Name) -ForegroundColor Yellow
}
}
Write-Host ("发现 {0} 个技能: {1}" -f $targets.Count, ($targets.Name -join ", "))
Write-Host ""
# --- 4. 对每个目标目录执行安装(备份旧版 -> 复制) ---
$timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
$totalBackup = 0
$totalFresh = 0
$failedTargets = @()
foreach ($destRel in $destRelList) {
$DestSkills = Join-Path $env:USERPROFILE $destRel
$BackupRoot = (Join-Path $env:USERPROFILE ($destRel -replace '\\skills$', '')) + "\skills-backup"
if ($DryRun) {
Write-Host "== 预览 [$env:USERPROFILE\$destRel](未执行)==" -ForegroundColor Cyan
$freshNames = @($targets | Where-Object { -not (Test-Path (Join-Path $DestSkills $_.Name)) })
$backupNames = @($targets | Where-Object { Test-Path (Join-Path $DestSkills $_.Name) })
if ($freshNames.Count -gt 0) {
Write-Host (" 新安装 {0}: {1}" -f $freshNames.Count, ($freshNames.Name -join ", "))
}
if ($backupNames.Count -gt 0) {
Write-Host (" 覆盖安装(旧版将备份){0}: {1}" -f $backupNames.Count, ($backupNames.Name -join ", "))
}
continue
}
Write-Host "--- 安装到 $DestSkills ---" -ForegroundColor Cyan
New-Item -ItemType Directory -Force -Path $DestSkills | Out-Null
foreach ($skill in $targets) {
$dest = Join-Path $DestSkills $skill.Name
try {
if (Test-Path $dest) {
New-Item -ItemType Directory -Force -Path $BackupRoot | Out-Null
$backupPath = Join-Path $BackupRoot ("{0}-{1}" -f $skill.Name, $timestamp)
Move-Item -Path $dest -Destination $backupPath
$totalBackup++
Write-Host "[备份] $($skill.Name) 旧版 -> $backupPath" -ForegroundColor Yellow
}
Copy-Item -Path $skill.FullName -Destination $dest -Recurse -Force
$totalFresh++
Write-Host "[安装] $($skill.Name)" -ForegroundColor Green
# 校验该技能落盘完整
if (-not (Test-Path (Join-Path $DestSkills "$($skill.Name)\SKILL.md"))) {
throw "SKILL.md 未落盘"
}
} catch {
Write-Host "[FAIL] $($skill.Name) -> $DestSkills : $_" -ForegroundColor Red
$failedTargets += "$destRel/$($skill.Name)"
}
}
}
# --- 5. 预览模式收尾 ---
if ($DryRun) {
Write-Host ""
Write-Host "[DryRun] 完成,未做任何修改。" -ForegroundColor Green
exit 0
}
# --- 6. 汇总与后续提示 ---
Write-Host ""
if ($failedTargets.Count -gt 0) {
Write-Host ("安装失败: {0}" -f ($failedTargets -join ", ")) -ForegroundColor Red
exit 2
}
Write-Host "全部安装成功。" -ForegroundColor Green
Write-Host ("本次共写入 {0} 个技能实例(新装含覆盖),备份 {1} 个旧版。" -f ($totalFresh), $totalBackup) -ForegroundColor Green
if ($warnCount -gt 0) {
Write-Host ("注意:{0} 处 name/文件夹不一致或缺 name 的可疑技能(见上方警告)。" -f $warnCount) -ForegroundColor Yellow
}
if ($totalBackup -gt 0) {
Write-Host "旧版本备份于各目标目录旁的 skills-backup确认新版可用后可手动删除" -ForegroundColor Yellow
}
Write-Host ""
Write-Host "下一步: 重启对应 agent 或新建会话后,在对话中提及技能名或相关意图即可触发:"
foreach ($s in $targets) {
Write-Host " - $($s.Name)"
}