microsoft/hve-core

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
feat/claude-support

Branches

Tags

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

Clone

HTTPS

Download ZIP

scripts/extension/Prepare-Extension.ps1

739lines · modecode

1#!/usr/bin/env pwsh
2# Copyright (c) Microsoft Corporation.
3# SPDX-License-Identifier: MIT
4#Requires -Version 7.0
5
6<#
7.SYNOPSIS
8 Prepares the HVE Core VS Code extension for packaging.
9
10.DESCRIPTION
11 This script prepares the VS Code extension by:
12 - Auto-discovering chat agents, prompts, and instruction files
13 - Filtering agents by maturity level based on channel
14 - Updating package.json with discovered components
15 - Updating changelog if provided
16
17 The package.json version is not modified.
18
19.PARAMETER ChangelogPath
20 Optional. Path to a changelog file to include in the package.
21
22.PARAMETER Channel
23 Optional. Release channel controlling which maturity levels are included.
24 'Stable' (default): Only includes agents with maturity 'stable'.
25 'PreRelease': Includes 'stable', 'preview', and 'experimental' maturity levels.
26
27.PARAMETER DryRun
28 Optional. If specified, shows what would be done without making changes.
29
30.EXAMPLE
31 ./Prepare-Extension.ps1
32 # Prepares stable channel using existing version from package.json
33
34.EXAMPLE
35 ./Prepare-Extension.ps1 -Channel PreRelease
36 # Prepares pre-release channel including experimental agents
37
38.EXAMPLE
39 ./Prepare-Extension.ps1 -ChangelogPath "./CHANGELOG.md"
40 # Prepares with changelog
41
42.NOTES
43 Dependencies: PowerShell-Yaml module
44#>
45
46[CmdletBinding()]
47param(
48 [Parameter(Mandatory = $false)]
49 [string]$ChangelogPath = "",
50
51 [Parameter(Mandatory = $false)]
52 [ValidateSet('Stable', 'PreRelease')]
53 [string]$Channel = 'Stable',
54
55 [Parameter(Mandatory = $false)]
56 [switch]$DryRun
57)
58
59$ErrorActionPreference = 'Stop'
60
61Import-Module (Join-Path $PSScriptRoot "../lib/Modules/CIHelpers.psm1") -Force
62
63#region Pure Functions
64
65function Get-AllowedMaturities {
66 <#
67 .SYNOPSIS
68 Returns allowed maturity levels based on release channel.
69 .DESCRIPTION
70 Pure function that determines which maturity levels (stable, preview, experimental)
71 are included in the extension package based on the specified channel.
72 .PARAMETER Channel
73 Release channel. 'Stable' returns only stable; 'PreRelease' includes all levels.
74 .OUTPUTS
75 [string[]] Array of allowed maturity level strings.
76 #>
77 [CmdletBinding()]
78 [OutputType([string[]])]
79 param(
80 [Parameter(Mandatory = $true)]
81 [ValidateSet('Stable', 'PreRelease')]
82 [string]$Channel
83 )
84
85 if ($Channel -eq 'PreRelease') {
86 return @('stable', 'preview', 'experimental')
87 }
88 return @('stable')
89}
90
91function Get-FrontmatterData {
92 <#
93 .SYNOPSIS
94 Extracts description and maturity from YAML frontmatter.
95 .DESCRIPTION
96 Function that parses YAML frontmatter from a markdown file
97 and returns a hashtable with description and maturity values.
98 .PARAMETER FilePath
99 Path to the markdown file to parse.
100 .PARAMETER FallbackDescription
101 Default description if none found in frontmatter.
102 .OUTPUTS
103 [hashtable] With description and maturity keys.
104 #>
105 [CmdletBinding()]
106 [OutputType([hashtable])]
107 param(
108 [Parameter(Mandatory = $true)]
109 [string]$FilePath,
110
111 [Parameter(Mandatory = $false)]
112 [string]$FallbackDescription = ""
113 )
114
115 $content = Get-Content -Path $FilePath -Raw
116 $description = ""
117 $maturity = "stable"
118
119 if ($content -match '(?s)^---\s*\r?\n(.*?)\r?\n---') {
120 $yamlContent = $Matches[1] -replace '\r\n', "`n" -replace '\r', "`n"
121 try {
122 $data = ConvertFrom-Yaml -Yaml $yamlContent
123 if ($data.ContainsKey('description')) {
124 $description = $data.description
125 }
126 if ($data.ContainsKey('maturity')) {
127 $maturity = $data.maturity
128 }
129 }
130 catch {
131 Write-Warning "Failed to parse YAML frontmatter in $(Split-Path -Leaf $FilePath): $_"
132 }
133 }
134
135 return @{
136 description = if ($description) { $description } else { $FallbackDescription }
137 maturity = $maturity
138 }
139}
140
141function Test-PathsExist {
142 <#
143 .SYNOPSIS
144 Validates that required paths exist for extension preparation.
145 .DESCRIPTION
146 Validation function that checks whether extension directory, package.json,
147 and .github directory exist at the specified locations.
148 .PARAMETER ExtensionDir
149 Path to the extension directory.
150 .PARAMETER PackageJsonPath
151 Path to package.json file.
152 .PARAMETER GitHubDir
153 Path to .github directory.
154 .OUTPUTS
155 [hashtable] With IsValid bool, MissingPaths array, and ErrorMessages array.
156 #>
157 [CmdletBinding()]
158 [OutputType([hashtable])]
159 param(
160 [Parameter(Mandatory = $true)]
161 [string]$ExtensionDir,
162
163 [Parameter(Mandatory = $true)]
164 [string]$PackageJsonPath,
165
166 [Parameter(Mandatory = $true)]
167 [string]$GitHubDir
168 )
169
170 $missingPaths = @()
171 $errorMessages = @()
172
173 if (-not (Test-Path $ExtensionDir)) {
174 $missingPaths += $ExtensionDir
175 $errorMessages += "Extension directory not found: $ExtensionDir"
176 }
177 if (-not (Test-Path $PackageJsonPath)) {
178 $missingPaths += $PackageJsonPath
179 $errorMessages += "package.json not found: $PackageJsonPath"
180 }
181 if (-not (Test-Path $GitHubDir)) {
182 $missingPaths += $GitHubDir
183 $errorMessages += ".github directory not found: $GitHubDir"
184 }
185
186 return @{
187 IsValid = ($missingPaths.Count -eq 0)
188 MissingPaths = $missingPaths
189 ErrorMessages = $errorMessages
190 }
191}
192
193function Get-DiscoveredAgents {
194 <#
195 .SYNOPSIS
196 Discovers chat agent files from the agents directory.
197 .DESCRIPTION
198 Discovery function that scans the agents directory for .agent.md files,
199 extracts frontmatter data, filters by maturity and exclusion list,
200 and returns structured agent objects.
201 .PARAMETER AgentsDir
202 Path to the agents directory.
203 .PARAMETER AllowedMaturities
204 Array of maturity levels to include.
205 .PARAMETER ExcludedAgents
206 Array of agent names to exclude from packaging.
207 .OUTPUTS
208 [hashtable] With Agents array, Skipped array, and DirectoryExists bool.
209 #>
210 [CmdletBinding()]
211 [OutputType([hashtable])]
212 param(
213 [Parameter(Mandatory = $true)]
214 [string]$AgentsDir,
215
216 [Parameter(Mandatory = $true)]
217 [string[]]$AllowedMaturities,
218
219 [Parameter(Mandatory = $false)]
220 [string[]]$ExcludedAgents = @()
221 )
222
223 $result = @{
224 Agents = @()
225 Skipped = @()
226 DirectoryExists = (Test-Path $AgentsDir)
227 }
228
229 if (-not $result.DirectoryExists) {
230 return $result
231 }
232
233 $agentFiles = Get-ChildItem -Path $AgentsDir -Filter "*.agent.md" | Sort-Object Name
234
235 foreach ($agentFile in $agentFiles) {
236 $agentName = $agentFile.BaseName -replace '\.agent$', ''
237
238 if ($ExcludedAgents -contains $agentName) {
239 $result.Skipped += @{ Name = $agentName; Reason = 'excluded' }
240 continue
241 }
242
243 $frontmatter = Get-FrontmatterData -FilePath $agentFile.FullName -FallbackDescription "AI agent for $agentName"
244 $maturity = $frontmatter.maturity
245
246 if ($AllowedMaturities -notcontains $maturity) {
247 $result.Skipped += @{ Name = $agentName; Reason = "maturity: $maturity" }
248 continue
249 }
250
251 $result.Agents += [PSCustomObject]@{
252 name = $agentName
253 path = "./.github/agents/$($agentFile.Name)"
254 description = $frontmatter.description
255 }
256 }
257
258 return $result
259}
260
261function Get-DiscoveredPrompts {
262 <#
263 .SYNOPSIS
264 Discovers prompt files from the prompts directory.
265 .DESCRIPTION
266 Discovery function that scans the prompts directory for .prompt.md files,
267 extracts frontmatter data, filters by maturity, and returns structured
268 prompt objects with relative paths.
269 .PARAMETER PromptsDir
270 Path to the prompts directory.
271 .PARAMETER GitHubDir
272 Path to the .github directory for relative path calculation.
273 .PARAMETER AllowedMaturities
274 Array of maturity levels to include.
275 .OUTPUTS
276 [hashtable] With Prompts array, Skipped array, and DirectoryExists bool.
277 #>
278 [CmdletBinding()]
279 [OutputType([hashtable])]
280 param(
281 [Parameter(Mandatory = $true)]
282 [string]$PromptsDir,
283
284 [Parameter(Mandatory = $true)]
285 [string]$GitHubDir,
286
287 [Parameter(Mandatory = $true)]
288 [string[]]$AllowedMaturities
289 )
290
291 $result = @{
292 Prompts = @()
293 Skipped = @()
294 DirectoryExists = (Test-Path $PromptsDir)
295 }
296
297 if (-not $result.DirectoryExists) {
298 return $result
299 }
300
301 $promptFiles = Get-ChildItem -Path $PromptsDir -Filter "*.prompt.md" -Recurse | Sort-Object Name
302
303 foreach ($promptFile in $promptFiles) {
304 $promptName = $promptFile.BaseName -replace '\.prompt$', ''
305 $displayName = ($promptName -replace '-', ' ') -replace '(\b\w)', { $_.Groups[1].Value.ToUpper() }
306 $frontmatter = Get-FrontmatterData -FilePath $promptFile.FullName -FallbackDescription "Prompt for $displayName"
307 $maturity = $frontmatter.maturity
308
309 if ($AllowedMaturities -notcontains $maturity) {
310 $result.Skipped += @{ Name = $promptName; Reason = "maturity: $maturity" }
311 continue
312 }
313
314 $relativePath = [System.IO.Path]::GetRelativePath($GitHubDir, $promptFile.FullName) -replace '\\', '/'
315
316 $result.Prompts += [PSCustomObject]@{
317 name = $promptName
318 path = "./.github/$relativePath"
319 description = $frontmatter.description
320 }
321 }
322
323 return $result
324}
325
326function Get-DiscoveredInstructions {
327 <#
328 .SYNOPSIS
329 Discovers instruction files from the instructions directory.
330 .DESCRIPTION
331 Discovery function that scans the instructions directory for .instructions.md files,
332 extracts frontmatter data, filters by maturity, and returns structured
333 instruction objects with normalized paths.
334 .PARAMETER InstructionsDir
335 Path to the instructions directory.
336 .PARAMETER GitHubDir
337 Path to the .github directory for relative path calculation.
338 .PARAMETER AllowedMaturities
339 Array of maturity levels to include.
340 .OUTPUTS
341 [hashtable] With Instructions array, Skipped array, and DirectoryExists bool.
342 #>
343 [CmdletBinding()]
344 [OutputType([hashtable])]
345 param(
346 [Parameter(Mandatory = $true)]
347 [string]$InstructionsDir,
348
349 [Parameter(Mandatory = $true)]
350 [string]$GitHubDir,
351
352 [Parameter(Mandatory = $true)]
353 [string[]]$AllowedMaturities
354 )
355
356 $result = @{
357 Instructions = @()
358 Skipped = @()
359 DirectoryExists = (Test-Path $InstructionsDir)
360 }
361
362 if (-not $result.DirectoryExists) {
363 return $result
364 }
365
366 $instructionFiles = Get-ChildItem -Path $InstructionsDir -Filter "*.instructions.md" -Recurse | Sort-Object Name
367
368 foreach ($instrFile in $instructionFiles) {
369 $baseName = $instrFile.BaseName -replace '\.instructions$', ''
370 $instrName = "$baseName-instructions"
371 $displayName = ($baseName -replace '-', ' ') -replace '(\b\w)', { $_.Groups[1].Value.ToUpper() }
372 $frontmatter = Get-FrontmatterData -FilePath $instrFile.FullName -FallbackDescription "Instructions for $displayName"
373 $maturity = $frontmatter.maturity
374
375 if ($AllowedMaturities -notcontains $maturity) {
376 $result.Skipped += @{ Name = $instrName; Reason = "maturity: $maturity" }
377 continue
378 }
379
380 $relativePathFromGitHub = [System.IO.Path]::GetRelativePath($GitHubDir, $instrFile.FullName)
381 $normalizedRelativePath = (Join-Path ".github" $relativePathFromGitHub) -replace '\\', '/'
382
383 $result.Instructions += [PSCustomObject]@{
384 name = $instrName
385 path = "./$normalizedRelativePath"
386 description = $frontmatter.description
387 }
388 }
389
390 return $result
391}
392
393function Update-PackageJsonContributes {
394 <#
395 .SYNOPSIS
396 Updates package.json contributes section with discovered components.
397 .DESCRIPTION
398 Pure function that takes a package.json object and discovered components,
399 returning a new object with the contributes section updated.
400 .PARAMETER PackageJson
401 The package.json object to update.
402 .PARAMETER ChatAgents
403 Array of discovered chat agent objects.
404 .PARAMETER ChatPromptFiles
405 Array of discovered prompt objects.
406 .PARAMETER ChatInstructions
407 Array of discovered instruction objects.
408 .OUTPUTS
409 [PSCustomObject] Updated package.json object.
410 #>
411 [CmdletBinding()]
412 [OutputType([PSCustomObject])]
413 param(
414 [Parameter(Mandatory = $true)]
415 [PSCustomObject]$PackageJson,
416
417 [Parameter(Mandatory = $true)]
418 [AllowEmptyCollection()]
419 [array]$ChatAgents,
420
421 [Parameter(Mandatory = $true)]
422 [AllowEmptyCollection()]
423 [array]$ChatPromptFiles,
424
425 [Parameter(Mandatory = $true)]
426 [AllowEmptyCollection()]
427 [array]$ChatInstructions
428 )
429
430 # Clone the object to avoid modifying the original
431 $updated = $PackageJson | ConvertTo-Json -Depth 10 | ConvertFrom-Json
432
433 # Ensure contributes section exists
434 if (-not $updated.contributes) {
435 $updated | Add-Member -NotePropertyName "contributes" -NotePropertyValue ([PSCustomObject]@{})
436 }
437
438 # Add or update contributes properties
439 if ($null -eq $updated.contributes.chatAgents) {
440 $updated.contributes | Add-Member -NotePropertyName "chatAgents" -NotePropertyValue $ChatAgents -Force
441 } else {
442 $updated.contributes.chatAgents = $ChatAgents
443 }
444
445 if ($null -eq $updated.contributes.chatPromptFiles) {
446 $updated.contributes | Add-Member -NotePropertyName "chatPromptFiles" -NotePropertyValue $ChatPromptFiles -Force
447 } else {
448 $updated.contributes.chatPromptFiles = $ChatPromptFiles
449 }
450
451 if ($null -eq $updated.contributes.chatInstructions) {
452 $updated.contributes | Add-Member -NotePropertyName "chatInstructions" -NotePropertyValue $ChatInstructions -Force
453 } else {
454 $updated.contributes.chatInstructions = $ChatInstructions
455 }
456
457 return $updated
458}
459
460function New-PrepareResult {
461 <#
462 .SYNOPSIS
463 Creates a standardized result object for extension preparation operations.
464 .DESCRIPTION
465 Factory function that creates a hashtable with consistent properties
466 for reporting preparation operation outcomes.
467 .PARAMETER Success
468 Indicates whether the operation completed successfully.
469 .PARAMETER Version
470 The version string from package.json.
471 .PARAMETER AgentCount
472 Number of agents discovered and included.
473 .PARAMETER PromptCount
474 Number of prompts discovered and included.
475 .PARAMETER InstructionCount
476 Number of instructions discovered and included.
477 .PARAMETER ErrorMessage
478 Error description when Success is false.
479 .OUTPUTS
480 Hashtable with Success, Version, AgentCount, PromptCount,
481 InstructionCount, and ErrorMessage properties.
482 #>
483 [CmdletBinding()]
484 [OutputType([hashtable])]
485 param(
486 [Parameter(Mandatory = $true)]
487 [bool]$Success,
488
489 [Parameter(Mandatory = $false)]
490 [string]$Version = "",
491
492 [Parameter(Mandatory = $false)]
493 [int]$AgentCount = 0,
494
495 [Parameter(Mandatory = $false)]
496 [int]$PromptCount = 0,
497
498 [Parameter(Mandatory = $false)]
499 [int]$InstructionCount = 0,
500
501 [Parameter(Mandatory = $false)]
502 [string]$ErrorMessage = ""
503 )
504
505 return @{
506 Success = $Success
507 Version = $Version
508 AgentCount = $AgentCount
509 PromptCount = $PromptCount
510 InstructionCount = $InstructionCount
511 ErrorMessage = $ErrorMessage
512 }
513}
514
515function Invoke-PrepareExtension {
516 <#
517 .SYNOPSIS
518 Orchestrates VS Code extension preparation with full error handling.
519 .DESCRIPTION
520 Executes the complete preparation workflow: validates paths, discovers
521 agents/prompts/instructions, updates package.json, and handles changelog.
522 Returns a result object instead of using exit codes.
523 .PARAMETER ExtensionDirectory
524 Absolute path to the extension directory containing package.json.
525 .PARAMETER RepoRoot
526 Absolute path to the repository root directory.
527 .PARAMETER Channel
528 Release channel controlling maturity filter ('Stable' or 'PreRelease').
529 .PARAMETER ChangelogPath
530 Optional path to changelog file to include.
531 .PARAMETER DryRun
532 When specified, shows what would be done without making changes.
533 .OUTPUTS
534 Hashtable with Success, Version, AgentCount, PromptCount,
535 InstructionCount, and ErrorMessage properties.
536 #>
537 [CmdletBinding()]
538 [OutputType([hashtable])]
539 param(
540 [Parameter(Mandatory = $true)]
541 [ValidateNotNullOrEmpty()]
542 [string]$ExtensionDirectory,
543
544 [Parameter(Mandatory = $true)]
545 [ValidateNotNullOrEmpty()]
546 [string]$RepoRoot,
547
548 [Parameter(Mandatory = $false)]
549 [ValidateSet('Stable', 'PreRelease')]
550 [string]$Channel = 'Stable',
551
552 [Parameter(Mandatory = $false)]
553 [string]$ChangelogPath = "",
554
555 [Parameter(Mandatory = $false)]
556 [switch]$DryRun
557 )
558
559 # Derive paths
560 $GitHubDir = Join-Path $RepoRoot ".github"
561 $PackageJsonPath = Join-Path $ExtensionDirectory "package.json"
562
563 # Validate required paths exist
564 $pathValidation = Test-PathsExist -ExtensionDir $ExtensionDirectory `
565 -PackageJsonPath $PackageJsonPath `
566 -GitHubDir $GitHubDir
567 if (-not $pathValidation.IsValid) {
568 $missingPaths = $pathValidation.MissingPaths -join ', '
569 return New-PrepareResult -Success $false -ErrorMessage "Required paths not found: $missingPaths"
570 }
571
572 # Read and parse package.json
573 try {
574 $packageJsonContent = Get-Content -Path $PackageJsonPath -Raw
575 $packageJson = $packageJsonContent | ConvertFrom-Json
576 }
577 catch {
578 return New-PrepareResult -Success $false -ErrorMessage "Failed to parse package.json at '$PackageJsonPath'. Check the file for JSON syntax errors. Underlying error: $($_.Exception.Message)"
579 }
580
581 # Validate version field
582 if (-not $packageJson.PSObject.Properties['version']) {
583 return New-PrepareResult -Success $false -ErrorMessage "package.json does not contain a 'version' field"
584 }
585 $version = $packageJson.version
586 if ($version -notmatch '^\d+\.\d+\.\d+$') {
587 return New-PrepareResult -Success $false -ErrorMessage "Invalid version format in package.json: $version"
588 }
589
590 # Get allowed maturities for channel
591 $allowedMaturities = Get-AllowedMaturities -Channel $Channel
592
593 Write-Host "`n=== Prepare Extension ===" -ForegroundColor Cyan
594 Write-Host "Extension Directory: $ExtensionDirectory"
595 Write-Host "Repository Root: $RepoRoot"
596 Write-Host "Channel: $Channel"
597 Write-Host "Allowed Maturities: $($allowedMaturities -join ', ')"
598 Write-Host "Version: $version"
599 if ($DryRun) {
600 Write-Host "[DRY RUN] No changes will be made" -ForegroundColor Yellow
601 }
602
603 # Discover agents
604 $agentsDir = Join-Path $GitHubDir "agents"
605 $agentResult = Get-DiscoveredAgents -AgentsDir $agentsDir -AllowedMaturities $allowedMaturities -ExcludedAgents @()
606 $chatAgents = $agentResult.Agents
607 $excludedAgents = $agentResult.Skipped
608
609 Write-Host "`n--- Chat Agents ---" -ForegroundColor Green
610 Write-Host "Found $($chatAgents.Count) agent(s) matching criteria"
611 if ($excludedAgents.Count -gt 0) {
612 Write-Host "Excluded $($excludedAgents.Count) agent(s) due to maturity filter" -ForegroundColor Yellow
613 }
614
615 # Discover prompts
616 $promptsDir = Join-Path $GitHubDir "prompts"
617 $promptResult = Get-DiscoveredPrompts -PromptsDir $promptsDir -GitHubDir $GitHubDir -AllowedMaturities $allowedMaturities
618 $chatPrompts = $promptResult.Prompts
619 $excludedPrompts = $promptResult.Skipped
620
621 Write-Host "`n--- Chat Prompts ---" -ForegroundColor Green
622 Write-Host "Found $($chatPrompts.Count) prompt(s) matching criteria"
623 if ($excludedPrompts.Count -gt 0) {
624 Write-Host "Excluded $($excludedPrompts.Count) prompt(s) due to maturity filter" -ForegroundColor Yellow
625 }
626
627 # Discover instructions
628 $instructionsDir = Join-Path $GitHubDir "instructions"
629 $instructionResult = Get-DiscoveredInstructions -InstructionsDir $instructionsDir -GitHubDir $GitHubDir -AllowedMaturities $allowedMaturities
630 $chatInstructions = $instructionResult.Instructions
631 $excludedInstructions = $instructionResult.Skipped
632
633 Write-Host "`n--- Chat Instructions ---" -ForegroundColor Green
634 Write-Host "Found $($chatInstructions.Count) instruction(s) matching criteria"
635 if ($excludedInstructions.Count -gt 0) {
636 Write-Host "Excluded $($excludedInstructions.Count) instruction(s) due to maturity filter" -ForegroundColor Yellow
637 }
638
639 # Update package.json
640 $packageJson = Update-PackageJsonContributes -PackageJson $packageJson `
641 -ChatAgents $chatAgents `
642 -ChatPromptFiles $chatPrompts `
643 -ChatInstructions $chatInstructions
644
645 # Write updated package.json
646 if (-not $DryRun) {
647 $packageJson | ConvertTo-Json -Depth 10 | Set-Content -Path $PackageJsonPath -Encoding UTF8NoBOM
648 Write-Host "`nUpdated package.json with discovered artifacts" -ForegroundColor Green
649 }
650 else {
651 Write-Host "`n[DRY RUN] Would update package.json with discovered artifacts" -ForegroundColor Yellow
652 }
653
654 # Handle changelog
655 if ($ChangelogPath -and (Test-Path $ChangelogPath)) {
656 $destChangelog = Join-Path $ExtensionDirectory "CHANGELOG.md"
657 if (-not $DryRun) {
658 Copy-Item -Path $ChangelogPath -Destination $destChangelog -Force
659 Write-Host "Copied changelog to extension directory" -ForegroundColor Green
660 }
661 else {
662 Write-Host "[DRY RUN] Would copy changelog to extension directory" -ForegroundColor Yellow
663 }
664 }
665 elseif ($ChangelogPath) {
666 Write-Warning "Changelog path specified but file not found: $ChangelogPath"
667 }
668
669 Write-Host "`n=== Preparation Complete ===" -ForegroundColor Cyan
670
671 return New-PrepareResult -Success $true `
672 -Version $version `
673 -AgentCount $chatAgents.Count `
674 -PromptCount $chatPrompts.Count `
675 -InstructionCount $chatInstructions.Count
676}
677
678#endregion Pure Functions
679
680#region Main Execution
681if ($MyInvocation.InvocationName -ne '.') {
682 try {
683 # Verify PowerShell-Yaml module is available
684 if (-not (Get-Module -ListAvailable -Name PowerShell-Yaml)) {
685 throw "Required module 'PowerShell-Yaml' is not installed."
686 }
687 Import-Module PowerShell-Yaml -ErrorAction Stop
688
689 # Resolve paths using $MyInvocation (must stay in entry point)
690 $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
691 $RepoRoot = (Get-Item "$ScriptDir/../..").FullName
692 $ExtensionDir = Join-Path $RepoRoot "extension"
693
694 # Resolve changelog path if provided
695 $resolvedChangelogPath = ""
696 if ($ChangelogPath) {
697 $resolvedChangelogPath = if ([System.IO.Path]::IsPathRooted($ChangelogPath)) {
698 $ChangelogPath
699 }
700 else {
701 Join-Path $RepoRoot $ChangelogPath
702 }
703 }
704
705 Write-Host "📦 HVE Core Extension Preparer" -ForegroundColor Cyan
706 Write-Host "==============================" -ForegroundColor Cyan
707 Write-Host " Channel: $Channel" -ForegroundColor Cyan
708 Write-Host ""
709
710 # Call orchestration function
711 $result = Invoke-PrepareExtension `
712 -ExtensionDirectory $ExtensionDir `
713 -RepoRoot $RepoRoot `
714 -Channel $Channel `
715 -ChangelogPath $resolvedChangelogPath `
716 -DryRun:$DryRun
717
718 if (-not $result.Success) {
719 throw $result.ErrorMessage
720 }
721
722 Write-Host ""
723 Write-Host "🎉 Done!" -ForegroundColor Green
724 Write-Host ""
725 Write-Host "📊 Summary:" -ForegroundColor Cyan
726 Write-Host " Agents: $($result.AgentCount)"
727 Write-Host " Prompts: $($result.PromptCount)"
728 Write-Host " Instructions: $($result.InstructionCount)"
729 Write-Host " Version: $($result.Version)"
730
731 exit 0
732 }
733 catch {
734 Write-Error "Prepare Extension failed: $($_.Exception.Message)"
735 Write-CIAnnotation -Message $_.Exception.Message -Level Error
736 exit 1
737 }
738}
739#endregion
740