microsoft/hve-core

Public

mirrored fromhttps://github.com/microsoft/hve-coreAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
copilot/sub-pr-185

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

scripts/extension/Prepare-Extension.ps1

389lines ยท modecode

1#!/usr/bin/env pwsh
2#Requires -Modules PowerShell-Yaml
3
4<#
5.SYNOPSIS
6 Prepares the HVE Core VS Code extension for packaging.
7
8.DESCRIPTION
9 This script prepares the VS Code extension by:
10 - Auto-discovering chat agents, chatmodes, prompts, and instruction files
11 - Filtering agents and chatmodes by maturity level based on channel
12 - Updating package.json with discovered components
13 - Updating changelog if provided
14
15 The package.json version is not modified.
16
17.PARAMETER ChangelogPath
18 Optional. Path to a changelog file to include in the package.
19
20.PARAMETER Channel
21 Optional. Release channel controlling which maturity levels are included.
22 'Stable' (default): Only includes agents/chatmodes with maturity 'stable'.
23 'PreRelease': Includes 'stable', 'preview', and 'experimental' maturity levels.
24
25.PARAMETER DryRun
26 Optional. If specified, shows what would be done without making changes.
27
28.EXAMPLE
29 ./Prepare-Extension.ps1
30 # Prepares stable channel using existing version from package.json
31
32.EXAMPLE
33 ./Prepare-Extension.ps1 -Channel PreRelease
34 # Prepares pre-release channel including experimental agents
35
36.EXAMPLE
37 ./Prepare-Extension.ps1 -ChangelogPath "./CHANGELOG.md"
38 # Prepares with changelog
39
40.NOTES
41 Dependencies: PowerShell-Yaml module
42#>
43
44[CmdletBinding()]
45param(
46 [Parameter(Mandatory = $false)]
47 [string]$ChangelogPath = "",
48
49 [Parameter(Mandatory = $false)]
50 [ValidateSet('Stable', 'PreRelease')]
51 [string]$Channel = 'Stable',
52
53 [Parameter(Mandatory = $false)]
54 [switch]$DryRun
55)
56
57$ErrorActionPreference = "Stop"
58
59# Define allowed maturity levels based on channel
60$allowedMaturities = if ($Channel -eq 'PreRelease') {
61 @('stable', 'preview', 'experimental')
62} else {
63 @('stable')
64}
65
66# Helper function to extract frontmatter data from YAML
67function Get-FrontmatterData {
68 param(
69 [Parameter(Mandatory = $true)]
70 [string]$FilePath,
71
72 [Parameter(Mandatory = $false)]
73 [string]$FallbackDescription = ""
74 )
75
76 $content = Get-Content -Path $FilePath -Raw
77 $description = ""
78 $maturity = "stable"
79
80 # Extract YAML frontmatter and parse with PowerShell-Yaml
81 if ($content -match '(?s)^---\s*\r?\n(.*?)\r?\n---') {
82 # Normalize line endings to LF for consistent parsing across platforms
83 $yamlContent = $Matches[1] -replace '\r\n', "`n" -replace '\r', "`n"
84 try {
85 $data = ConvertFrom-Yaml -Yaml $yamlContent
86 if ($data.ContainsKey('description')) {
87 $description = $data.description
88 }
89 if ($data.ContainsKey('maturity')) {
90 $maturity = $data.maturity
91 }
92 } catch {
93 Write-Warning "Failed to parse YAML frontmatter in $(Split-Path -Leaf $FilePath): $_"
94 }
95 }
96
97 # Return hashtable with description and maturity
98 return @{
99 description = if ($description) { $description } else { $FallbackDescription }
100 maturity = $maturity
101 }
102}
103
104# Determine script and repo paths
105$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
106$RepoRoot = (Get-Item "$ScriptDir/../..").FullName
107$ExtensionDir = Join-Path $RepoRoot "extension"
108$GitHubDir = Join-Path $RepoRoot ".github"
109$PackageJsonPath = Join-Path $ExtensionDir "package.json"
110
111Write-Host "๐Ÿ“ฆ HVE Core Extension Preparer" -ForegroundColor Cyan
112Write-Host "==============================" -ForegroundColor Cyan
113Write-Host " Channel: $Channel" -ForegroundColor Cyan
114Write-Host ""
115
116# Verify paths exist
117if (-not (Test-Path $ExtensionDir)) {
118 Write-Error "Extension directory not found: $ExtensionDir"
119 exit 1
120}
121
122if (-not (Test-Path $PackageJsonPath)) {
123 Write-Error "package.json not found: $PackageJsonPath"
124 exit 1
125}
126
127if (-not (Test-Path $GitHubDir)) {
128 Write-Error ".github directory not found: $GitHubDir"
129 exit 1
130}
131
132# Read current package.json
133Write-Host "๐Ÿ“– Reading package.json..." -ForegroundColor Yellow
134try {
135 $packageJson = Get-Content -Path $PackageJsonPath -Raw | ConvertFrom-Json
136} catch {
137 Write-Error "Failed to parse package.json: $_`nPlease check $PackageJsonPath for JSON syntax errors."
138 exit 1
139}
140
141# Validate package.json has required version field
142if (-not $packageJson.PSObject.Properties['version']) {
143 Write-Error "package.json is missing required 'version' field"
144 exit 1
145}
146
147# Use existing version from package.json
148$version = $packageJson.version
149
150# Validate version format
151if ($version -notmatch '^\d+\.\d+\.\d+$') {
152 Write-Error "Invalid version format in package.json: '$version'. Expected semantic version format (e.g., 1.0.0)"
153 exit 1
154}
155
156Write-Host " Using version: $version" -ForegroundColor Green
157
158# Discover chat agents (excluding hve-core-installer which is for manual installation only)
159Write-Host ""
160Write-Host "๐Ÿ” Discovering chat agents..." -ForegroundColor Yellow
161$agentsDir = Join-Path $GitHubDir "agents"
162$chatAgents = @()
163
164# Agents to exclude from extension packaging
165$excludedAgents = @('hve-core-installer')
166
167if (Test-Path $agentsDir) {
168 $agentFiles = Get-ChildItem -Path $agentsDir -Filter "*.agent.md" | Sort-Object Name
169
170 foreach ($agentFile in $agentFiles) {
171 # Extract agent name from filename (e.g., hve-core-installer.agent.md -> hve-core-installer)
172 $agentName = $agentFile.BaseName -replace '\.agent$', ''
173
174 # Skip excluded agents
175 if ($excludedAgents -contains $agentName) {
176 Write-Host " โญ๏ธ $agentName (excluded)" -ForegroundColor DarkGray
177 continue
178 }
179
180 # Extract frontmatter data
181 $frontmatter = Get-FrontmatterData -FilePath $agentFile.FullName -FallbackDescription "AI agent for $agentName"
182 $description = $frontmatter.description
183 $maturity = $frontmatter.maturity
184
185 # Filter by maturity based on channel
186 if ($allowedMaturities -notcontains $maturity) {
187 Write-Host " โญ๏ธ $agentName (maturity: $maturity, skipped for $Channel)" -ForegroundColor DarkGray
188 continue
189 }
190
191 $agent = [PSCustomObject]@{
192 name = $agentName
193 path = "./.github/agents/$($agentFile.Name)"
194 description = $description
195 }
196
197 $chatAgents += $agent
198 Write-Host " โœ… $agentName" -ForegroundColor Green
199 }
200} else {
201 Write-Warning "Agents directory not found: $agentsDir"
202}
203
204# Discover chatmodes
205Write-Host ""
206Write-Host "๐Ÿ” Discovering chatmodes..." -ForegroundColor Yellow
207$chatmodesDir = Join-Path $GitHubDir "chatmodes"
208$chatmodes = @()
209
210if (Test-Path $chatmodesDir) {
211 $chatmodeFiles = Get-ChildItem -Path $chatmodesDir -Filter "*.chatmode.md" | Sort-Object Name
212
213 foreach ($chatmodeFile in $chatmodeFiles) {
214 # Extract chatmode name from filename (e.g., task-planner.chatmode.md -> task-planner)
215 $chatmodeName = $chatmodeFile.BaseName -replace '\.chatmode$', ''
216
217 # Extract frontmatter data
218 $displayName = $chatmodeName -replace '-', ' '
219 $frontmatter = Get-FrontmatterData -FilePath $chatmodeFile.FullName -FallbackDescription "Chatmode for $displayName"
220 $description = $frontmatter.description
221 $maturity = $frontmatter.maturity
222
223 # Filter by maturity based on channel
224 if ($allowedMaturities -notcontains $maturity) {
225 Write-Host " โญ๏ธ $chatmodeName (maturity: $maturity, skipped for $Channel)" -ForegroundColor DarkGray
226 continue
227 }
228
229 $chatmode = [PSCustomObject]@{
230 name = $chatmodeName
231 path = "./.github/chatmodes/$($chatmodeFile.Name)"
232 description = $description
233 }
234
235 $chatmodes += $chatmode
236 Write-Host " โœ… $chatmodeName" -ForegroundColor Green
237 }
238} else {
239 Write-Warning "Chatmodes directory not found: $chatmodesDir"
240}
241
242# Discover prompts
243Write-Host ""
244Write-Host "๐Ÿ” Discovering prompts..." -ForegroundColor Yellow
245$promptsDir = Join-Path $GitHubDir "prompts"
246$chatPromptFiles = @()
247
248if (Test-Path $promptsDir) {
249 $promptFiles = Get-ChildItem -Path $promptsDir -Filter "*.prompt.md" -Recurse | Sort-Object Name
250
251 foreach ($promptFile in $promptFiles) {
252 # Extract prompt name from filename (e.g., git-commit.prompt.md -> git-commit)
253 $promptName = $promptFile.BaseName -replace '\.prompt$', ''
254
255 # Extract frontmatter data
256 $displayName = ($promptName -replace '-', ' ') -replace '(\b\w)', { $_.Groups[1].Value.ToUpper() }
257 $frontmatter = Get-FrontmatterData -FilePath $promptFile.FullName -FallbackDescription "Prompt for $displayName"
258 $description = $frontmatter.description
259 $maturity = $frontmatter.maturity
260
261 # Filter by maturity based on channel
262 if ($allowedMaturities -notcontains $maturity) {
263 Write-Host " โญ๏ธ $promptName (maturity: $maturity, skipped for $Channel)" -ForegroundColor DarkGray
264 continue
265 }
266
267 # Calculate relative path from .github
268 $relativePath = [System.IO.Path]::GetRelativePath($GitHubDir, $promptFile.FullName) -replace '\\', '/'
269
270 $prompt = [PSCustomObject]@{
271 name = $promptName
272 path = "./.github/$relativePath"
273 description = $description
274 }
275
276 $chatPromptFiles += $prompt
277 Write-Host " โœ… $promptName" -ForegroundColor Green
278 }
279} else {
280 Write-Warning "Prompts directory not found: $promptsDir"
281}
282
283# Discover instruction files
284Write-Host ""
285Write-Host "๐Ÿ” Discovering instruction files..." -ForegroundColor Yellow
286$instructionsDir = Join-Path $GitHubDir "instructions"
287$chatInstructions = @()
288
289if (Test-Path $instructionsDir) {
290 $instructionFiles = Get-ChildItem -Path $instructionsDir -Filter "*.instructions.md" -Recurse | Sort-Object Name
291
292 foreach ($instrFile in $instructionFiles) {
293 # Extract instruction name from filename (e.g., commit-message.instructions.md -> commit-message-instructions)
294 $baseName = $instrFile.BaseName -replace '\.instructions$', ''
295 $instrName = "$baseName-instructions"
296
297 # Extract frontmatter data
298 $displayName = ($baseName -replace '-', ' ') -replace '(\b\w)', { $_.Groups[1].Value.ToUpper() }
299 $frontmatter = Get-FrontmatterData -FilePath $instrFile.FullName -FallbackDescription "Instructions for $displayName"
300 $description = $frontmatter.description
301 $maturity = $frontmatter.maturity
302
303 # Filter by maturity based on channel
304 if ($allowedMaturities -notcontains $maturity) {
305 Write-Host " โญ๏ธ $instrName (maturity: $maturity, skipped for $Channel)" -ForegroundColor DarkGray
306 continue
307 }
308
309 # Calculate relative path from .github using cross-platform APIs
310 $relativePathFromGitHub = [System.IO.Path]::GetRelativePath($GitHubDir, $instrFile.FullName)
311 $normalizedRelativePath = (Join-Path ".github" $relativePathFromGitHub) -replace '\\', '/'
312
313 $instruction = [PSCustomObject]@{
314 name = $instrName
315 path = "./$normalizedRelativePath"
316 description = $description
317 }
318
319 $chatInstructions += $instruction
320 Write-Host " โœ… $instrName" -ForegroundColor Green
321 }
322} else {
323 Write-Warning "Instructions directory not found: $instructionsDir"
324}
325
326# Update package.json
327Write-Host ""
328Write-Host "๐Ÿ“ Updating package.json..." -ForegroundColor Yellow
329
330# Ensure contributes section exists
331if (-not $packageJson.contributes) {
332 $packageJson | Add-Member -NotePropertyName "contributes" -NotePropertyValue ([PSCustomObject]@{})
333}
334
335# Combine agents and chatmodes into chatAgents (VS Code treats chatmodes as chatAgents)
336$allChatAgents = $chatAgents + $chatmodes
337
338# Update chatAgents
339$packageJson.contributes.chatAgents = $allChatAgents
340Write-Host " Updated chatAgents: $($allChatAgents.Count) items ($($chatAgents.Count) agents + $($chatmodes.Count) chatmodes)" -ForegroundColor Green
341
342# Update chatPromptFiles
343$packageJson.contributes.chatPromptFiles = $chatPromptFiles
344Write-Host " Updated chatPromptFiles: $($chatPromptFiles.Count) prompts" -ForegroundColor Green
345
346# Update chatInstructions
347$packageJson.contributes.chatInstructions = $chatInstructions
348Write-Host " Updated chatInstructions: $($chatInstructions.Count) files" -ForegroundColor Green
349
350if ($DryRun) {
351 Write-Host ""
352 Write-Host "๐Ÿ” DRY RUN - Would write the following package.json:" -ForegroundColor Magenta
353 Write-Host ($packageJson | ConvertTo-Json -Depth 10)
354 Write-Host ""
355 Write-Host "๐Ÿ” DRY RUN - No changes made" -ForegroundColor Magenta
356 exit 0
357}
358
359# Write updated package.json
360$packageJson | ConvertTo-Json -Depth 10 | Set-Content -Path $PackageJsonPath -Encoding UTF8NoBOM
361Write-Host " Saved package.json" -ForegroundColor Green
362
363# Handle changelog if provided
364if ($ChangelogPath) {
365 Write-Host ""
366 Write-Host "๐Ÿ“‹ Processing changelog..." -ForegroundColor Yellow
367
368 if (Test-Path $ChangelogPath) {
369 $changelogDest = Join-Path $ExtensionDir "CHANGELOG.md"
370 Copy-Item -Path $ChangelogPath -Destination $changelogDest -Force
371 Write-Host " Copied changelog to extension directory" -ForegroundColor Green
372 } else {
373 Write-Warning "Changelog file not found: $ChangelogPath"
374 }
375}
376
377Write-Host ""
378Write-Host "๐ŸŽ‰ Done!" -ForegroundColor Green
379Write-Host ""
380Write-Host "๐Ÿ“Š Summary:" -ForegroundColor Cyan
381Write-Host " Version: $version" -ForegroundColor White
382Write-Host " Channel: $Channel" -ForegroundColor White
383Write-Host " Chat Agents: $($chatAgents.Count)" -ForegroundColor White
384Write-Host " Chatmodes: $($chatmodes.Count)" -ForegroundColor White
385Write-Host " Prompts: $($chatPromptFiles.Count)" -ForegroundColor White
386Write-Host " Instructions: $($chatInstructions.Count)" -ForegroundColor White
387Write-Host ""
388
389exit 0
390