198 lines
7.4 KiB
PowerShell
198 lines
7.4 KiB
PowerShell
# Builds dist\cursor\ from skills\: flat skill copies, mcp snippets, one plugin per skill.
|
|
# Usage: scripts\build-cursor.ps1 [-Name <skill-name>]
|
|
param([string]$Name)
|
|
. (Join-Path $PSScriptRoot 'lib.ps1')
|
|
. (Join-Path $PSScriptRoot 'cursor-deeplink.ps1')
|
|
|
|
$CursorDist = Join-Path $DistDir 'cursor'
|
|
$ServerKeys = @{
|
|
'wordcraft-mcp' = 'wordcraft'
|
|
'gridcraft-mcp' = 'gridcraft'
|
|
}
|
|
|
|
function Get-McpServerKey([string]$SkillName) {
|
|
if ($ServerKeys.ContainsKey($SkillName)) { return $ServerKeys[$SkillName] }
|
|
# fallback: strip trailing -mcp
|
|
if ($SkillName -match '^(.+)-mcp$') { return $Matches[1] }
|
|
return $SkillName
|
|
}
|
|
|
|
function Convert-ClaudeArgsToCursor([object[]]$CliArgs) {
|
|
$out = New-Object System.Collections.Generic.List[string]
|
|
foreach ($a in $CliArgs) {
|
|
$s = [string]$a
|
|
if ($s -eq '${user_config.author}') { $out.Add('${AUTHOR}'); continue }
|
|
if ($s -eq '${user_config.read_root}') { $out.Add('${READ_ROOT}'); continue }
|
|
if ($s -eq '${user_config.write_root}') { $out.Add('${WRITE_ROOT}'); continue }
|
|
$out.Add($s)
|
|
}
|
|
return , $out.ToArray()
|
|
}
|
|
|
|
function Convert-ArgsToUserHomeDefaults([object[]]$CliArgs) {
|
|
$out = New-Object System.Collections.Generic.List[string]
|
|
foreach ($a in $CliArgs) {
|
|
$s = [string]$a
|
|
if ($s -eq '${user_config.author}' -or $s -eq '${AUTHOR}') { $out.Add('Claude'); continue }
|
|
if ($s -eq '${user_config.read_root}' -or $s -eq '${READ_ROOT}') { $out.Add('${userHome}/Documents'); continue }
|
|
if ($s -eq '${user_config.write_root}' -or $s -eq '${WRITE_ROOT}') { $out.Add('${userHome}/Documents/Claude'); continue }
|
|
$out.Add($s)
|
|
}
|
|
return , $out.ToArray()
|
|
}
|
|
|
|
function Build-OneSkill([string]$N) {
|
|
$dir = Join-Path $SkillsDir $N
|
|
if (-not (Test-Path $dir -PathType Container)) {
|
|
Write-Host "skills/$N fehlt."
|
|
return $false
|
|
}
|
|
$ver = Get-SkillVersion $dir
|
|
if (-not $ver) {
|
|
Write-Host "CHANGELOG von $N hat keine Version."
|
|
return $false
|
|
}
|
|
|
|
$skillPath = Join-Path $dir 'SKILL.md'
|
|
$fm = Get-Frontmatter $skillPath
|
|
if ($null -eq $fm) {
|
|
Write-Host "${N}: kein Frontmatter."
|
|
return $false
|
|
}
|
|
$fmName = Get-FmValue $fm 'name'
|
|
$desc = Get-FmValue $fm 'description'
|
|
if ($fmName -cne $N) {
|
|
Write-Host "${N}: Frontmatter name '$fmName' != Ordner."
|
|
return $false
|
|
}
|
|
if (-not $desc -or $desc.Length -gt 1024) {
|
|
Write-Host "${N}: description fehlt oder > 1024."
|
|
return $false
|
|
}
|
|
|
|
# Flat skill copy
|
|
$flatDir = Join-Path (Join-Path $CursorDist 'skills') $N
|
|
New-Item -ItemType Directory -Force -Path $flatDir | Out-Null
|
|
Copy-Item -LiteralPath $skillPath -Destination (Join-Path $flatDir 'SKILL.md') -Force
|
|
|
|
$extManPath = Join-Path $dir 'extension\manifest.json'
|
|
$hasExt = Test-Path $extManPath
|
|
$pluginRoot = Join-Path (Join-Path $CursorDist 'plugins') $N
|
|
if (Test-Path $pluginRoot) { Remove-Item $pluginRoot -Recurse -Force }
|
|
New-Item -ItemType Directory -Force -Path (Join-Path $pluginRoot '.cursor-plugin') | Out-Null
|
|
$pluginSkillDir = Join-Path (Join-Path $pluginRoot 'skills') $N
|
|
New-Item -ItemType Directory -Force -Path $pluginSkillDir | Out-Null
|
|
Copy-Item -LiteralPath $skillPath -Destination (Join-Path $pluginSkillDir 'SKILL.md') -Force
|
|
|
|
$displayDesc = $desc
|
|
$authorName = 'Dominic Gundel'
|
|
$pluginVars = $null
|
|
$mcpForPlugin = $null
|
|
$mcpStandalone = $null
|
|
$serverKey = Get-McpServerKey $N
|
|
|
|
if ($hasExt) {
|
|
$man = (Read-Text $extManPath) | ConvertFrom-Json
|
|
if ($man.version -cne $ver) {
|
|
Write-Host "${N}: manifest version '$($man.version)' != CHANGELOG '$ver'."
|
|
return $false
|
|
}
|
|
$dn = [string]$man.display_name
|
|
if ($dn -match ' for Claude$') {
|
|
$displayDesc = ($dn -replace ' for Claude$', ' for Cursor') + '. ' + $desc
|
|
}
|
|
elseif ($man.description) {
|
|
$displayDesc = [string]$man.description
|
|
}
|
|
if ($man.author -and $man.author.name) { $authorName = [string]$man.author.name }
|
|
|
|
$cmd = [string]$man.server.mcp_config.command
|
|
$argsRaw = @($man.server.mcp_config.args)
|
|
$argsPlugin = Convert-ClaudeArgsToCursor $argsRaw
|
|
$argsHome = Convert-ArgsToUserHomeDefaults $argsRaw
|
|
|
|
$transportPlugin = @{ command = $cmd; args = @($argsPlugin) }
|
|
$transportHome = @{ command = $cmd; args = @($argsHome) }
|
|
|
|
$mcpForPlugin = @{ mcpServers = @{ $serverKey = $transportPlugin } }
|
|
$mcpStandalone = @{ mcpServers = @{ $serverKey = $transportHome } }
|
|
|
|
if ($man.user_config) {
|
|
$props = @{}
|
|
$required = @()
|
|
if ($man.user_config.read_root) {
|
|
$props['READ_ROOT'] = @{
|
|
type = 'string'
|
|
title = 'Lese-Ordner'
|
|
description = 'Weit fassen (Standard: Dokumente).'
|
|
default = '${userHome}/Documents'
|
|
}
|
|
$required += 'READ_ROOT'
|
|
}
|
|
if ($man.user_config.write_root) {
|
|
$props['WRITE_ROOT'] = @{
|
|
type = 'string'
|
|
title = 'Schreib-Ordner'
|
|
description = 'Eng halten (Standard: Dokumente/Claude).'
|
|
default = '${userHome}/Documents/Claude'
|
|
}
|
|
$required += 'WRITE_ROOT'
|
|
}
|
|
if ($man.user_config.author) {
|
|
$props['AUTHOR'] = @{
|
|
type = 'string'
|
|
title = 'Autorname'
|
|
description = 'Name fuer nachverfolgte Aenderungen.'
|
|
default = 'Claude'
|
|
}
|
|
}
|
|
if ($props.Count -gt 0) {
|
|
$pluginVars = @{
|
|
type = 'object'
|
|
properties = $props
|
|
}
|
|
if ($required.Count -gt 0) { $pluginVars['required'] = $required }
|
|
}
|
|
}
|
|
|
|
$mcpDir = Join-Path $CursorDist 'mcp'
|
|
New-Item -ItemType Directory -Force -Path $mcpDir | Out-Null
|
|
$snippetPath = Join-Path $mcpDir "$N.json"
|
|
Write-Text $snippetPath (($mcpStandalone | ConvertTo-Json -Depth 20) + "`n")
|
|
}
|
|
|
|
$pluginJson = @{
|
|
name = $N
|
|
version = $ver
|
|
description = $displayDesc
|
|
author = @{ name = $authorName }
|
|
license = 'MIT'
|
|
homepage = 'https://git.gundel.dev/dg/claude-skills'
|
|
repository = 'https://git.gundel.dev/dg/claude-skills.git'
|
|
}
|
|
if ($pluginVars) { $pluginJson['variables'] = $pluginVars }
|
|
|
|
Write-Text (Join-Path $pluginRoot '.cursor-plugin\plugin.json') (($pluginJson | ConvertTo-Json -Depth 20) + "`n")
|
|
if ($mcpForPlugin) {
|
|
Write-Text (Join-Path $pluginRoot 'mcp.json') (($mcpForPlugin | ConvertTo-Json -Depth 20) + "`n")
|
|
}
|
|
|
|
# Validate JSON round-trip
|
|
$null = (Read-Text (Join-Path $pluginRoot '.cursor-plugin\plugin.json')) | ConvertFrom-Json
|
|
if ($mcpForPlugin) {
|
|
$null = (Read-Text (Join-Path $pluginRoot 'mcp.json')) | ConvertFrom-Json
|
|
}
|
|
|
|
Write-Host "OK cursor plugin $N $ver"
|
|
return $true
|
|
}
|
|
|
|
if ($Name) { $names = @($Name) } else { $names = @(Get-SkillNames) }
|
|
New-Item -ItemType Directory -Force -Path $CursorDist | Out-Null
|
|
$ok = $true
|
|
foreach ($n in $names) {
|
|
if (-not (Build-OneSkill $n)) { $ok = $false }
|
|
}
|
|
if (-not $ok) { exit 1 }
|
|
exit 0
|