Files
HolodoriCalc/scripts/package-holodori-web-skill.ps1
T
dal4segno 2b26bbd94f
Publish GitHub Pages output / publish (push) Successful in 43s
feat: add browser AI connection guide and calculator instructions
2026-10-03 15:45:37 +09:00

77 lines
5.5 KiB
PowerShell

$ErrorActionPreference = 'Stop'
$taskRoot = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..'))
$pluginRoot = Join-Path $taskRoot 'plugins\holodori-web-assistant'
$skillRoot = Join-Path $pluginRoot 'skills\holodori-web-assistant'
$outputRoot = Join-Path $taskRoot 'plugin-dist'
$manifest = Get-Content -LiteralPath (Join-Path $pluginRoot 'plugin.json') -Raw -Encoding UTF8 | ConvertFrom-Json
if ($manifest.name -ne 'holodori-web-assistant' -or $manifest.version -notmatch '^\d+\.\d+\.\d+$') { throw 'Invalid plugin identity.' }
if ($manifest.extensions.'com.openai'.interface.shortDescription.Length -gt 30) { throw 'Plugin subtitle is too long.' }
$allowed = @('plugin.json', 'assets/icon.svg', 'skills/holodori-web-assistant/SKILL.md', 'skills/holodori-web-assistant/agents/openai.yaml', 'skills/holodori-web-assistant/references/requests.md', 'skills/holodori-web-assistant/references/browser-workflow.md')
$sourceFiles = @(Get-ChildItem -LiteralPath $pluginRoot -Recurse -File)
$paths = @($sourceFiles | ForEach-Object { [System.IO.Path]::GetRelativePath($pluginRoot, $_.FullName).Replace('\', '/') })
if ($paths.Count -ne $allowed.Count -or (Compare-Object $paths $allowed)) { throw 'The instruction package must contain only its declared files.' }
$skill = Get-Content -LiteralPath (Join-Path $skillRoot 'SKILL.md') -Raw -Encoding UTF8
if ($skill -notmatch '^---\r?\nname: holodori-web-assistant\r?\ndescription: [^\r\n]+\r?\n---') { throw 'Invalid skill frontmatter.' }
New-Item -ItemType Directory -Path $outputRoot -Force | Out-Null
$pluginZip = Join-Path $outputRoot 'holodori-web-assistant-plugin.zip'
$skillZip = Join-Path $outputRoot 'holodori-web-assistant-skill.zip'
Compress-Archive -Path (Join-Path $pluginRoot '*') -DestinationPath $pluginZip -Force
Compress-Archive -LiteralPath $skillRoot -DestinationPath $skillZip -Force
Add-Type -AssemblyName System.IO.Compression.FileSystem
foreach ($archive in @($pluginZip, $skillZip)) {
$zip = [System.IO.Compression.ZipFile]::OpenRead($archive)
try {
$expected = if ($archive -eq $pluginZip) { $allowed } else { @('holodori-web-assistant/SKILL.md', 'holodori-web-assistant/agents/openai.yaml', 'holodori-web-assistant/references/requests.md', 'holodori-web-assistant/references/browser-workflow.md') }
$actual = @($zip.Entries | Where-Object { -not $_.FullName.EndsWith('/') } | ForEach-Object { $_.FullName })
if (Compare-Object $actual $expected) { throw 'Unexpected ZIP contents.' }
foreach ($entry in $zip.Entries) {
if ($entry.FullName.EndsWith('/')) { continue }
$relative = if ($archive -eq $pluginZip) { $entry.FullName } else { 'skills/' + $entry.FullName }
$source = Join-Path $pluginRoot $relative
$stream = $entry.Open()
$hasher = [System.Security.Cryptography.SHA256]::Create()
try { $archiveHash = [Convert]::ToHexString($hasher.ComputeHash($stream)) }
finally { $stream.Dispose(); $hasher.Dispose() }
if ($archiveHash -ne (Get-FileHash -LiteralPath $source -Algorithm SHA256).Hash) { throw 'Archive hash mismatch.' }
}
Write-Output "Verified $archive ($($actual.Count) files; $((Get-Item -LiteralPath $archive).Length) bytes)."
} finally { $zip.Dispose() }
}
$downloadRoot = Join-Path $taskRoot 'static\downloads'
New-Item -ItemType Directory -Path $downloadRoot -Force | Out-Null
foreach ($archive in @($pluginZip, $skillZip)) {
$destination = Join-Path $downloadRoot ([System.IO.Path]::GetFileName($archive))
Copy-Item -LiteralPath $archive -Destination $destination -Force
if ((Get-FileHash -LiteralPath $archive).Hash -ne (Get-FileHash -LiteralPath $destination).Hash) { throw 'Download copy hash mismatch.' }
Write-Output "Prepared website download: $destination"
}
$guideRoot = Join-Path $taskRoot 'static\ai'
New-Item -ItemType Directory -Path $guideRoot -Force | Out-Null
$guide = $skill.Replace('[references/browser-workflow.md](references/browser-workflow.md)', '[Browser calculation workflow](#browser-calculation-workflow)').Replace('[references/requests.md](references/requests.md)', '[Analysis requests](#analysis-requests)')
foreach ($reference in @('browser-workflow.md', 'requests.md')) {
$guide += "`n`n" + (Get-Content -LiteralPath (Join-Path $skillRoot "references\$reference") -Raw -Encoding UTF8)
}
if ($guide -match '\]\(references/') { throw 'The standalone guide must not require separate reference files.' }
$guidePath = Join-Path $guideRoot 'SKILL.md'
[System.IO.File]::WriteAllText($guidePath, $guide, [System.Text.UTF8Encoding]::new($false))
$escapedGuide = [System.Net.WebUtility]::HtmlEncode($guide)
$html = @"
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>SKILL.md · HolodoriCalc AI usage guide</title>
<style>body{max-width:960px;margin:32px auto;padding:0 24px;background:#11151b;color:#e5e7eb;font:16px/1.7 system-ui,sans-serif}a{color:#f3a5c5}pre{white-space:pre-wrap;overflow-wrap:anywhere;font:14px/1.7 ui-monospace,monospace}</style>
</head>
<body>
<h1>HolodoriCalc · SKILL.md</h1>
<p>GPT용 사용 안내입니다. 아래 내용을 읽고 원래 계산기 탭에서 작업하세요. <a href="./SKILL.md" download>SKILL.md 다운로드</a></p>
<pre aria-label="SKILL.md">$escapedGuide</pre>
</body>
</html>
"@
[System.IO.File]::WriteAllText((Join-Path $guideRoot 'SKILL.html'), $html, [System.Text.UTF8Encoding]::new($false))
if ((Get-Content -LiteralPath $guidePath -Raw -Encoding UTF8) -ne $guide) { throw 'Standalone guide content mismatch.' }
Write-Output "Prepared standalone AI guide: $guidePath"