Skip to content
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
- `Deploy-NovaPackage` now shows a concise resolved-upload summary before execution, reports progress while multiple artifacts are uploading, and prints a short completion summary with a suggested verification step after successful raw uploads.
- `Get-NovaProjectInfo` now fails with clearer recovery guidance when `-Path` does not exist, points to a file, or the target folder is missing `project.json`.
- `Get-NovaUpdateNotificationPreference -Verbose` now explains whether Nova is reading a stored preference or the built-in default, and the command help now points read-only PowerShell users to the matching `% nova notification` workflow.
- `Initialize-NovaModule` now shows scaffold progress, ends with the created project root plus the next suggested cmdlet, and fails with clearer guidance when the target project folder already exists.

### Deprecated

Expand Down
1 change: 1 addition & 0 deletions RELEASE_NOTE.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ This file summarizes the release notes for NovaModuleTools. **UNRELEASED** chang
- `Deploy-NovaPackage` now shows clearer terminal feedback during raw package uploads, including a concise pre-flight summary, progress across multiple artifacts, and a short verification hint after success.
- `Get-NovaProjectInfo` now explains how to recover when `-Path` is invalid or the target folder is not a Nova project root.
- `Get-NovaUpdateNotificationPreference -Verbose` now tells you whether Nova is using a stored update-notification preference or the built-in default, and the help now points to the matching `% nova notification` workflow.
- `Initialize-NovaModule` now shows scaffold progress, ends with the project root and the next suggested cmdlet, and gives clearer recovery guidance when the target project folder already exists.

### Deprecated

Expand Down
4 changes: 4 additions & 0 deletions docs/NovaModuleTools/en-US/Initialize-NovaModule.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ Use `-Example` when you want the scaffold to start from the packaged example pro

This command supports `-WhatIf` and `-Confirm` through PowerShell `SupportsShouldProcess`. Use `-WhatIf` to preview the scaffold target after the interactive answers have been collected, without creating folders, writing `project.json`, or initializing Git.

During scaffold creation, Nova shows progress for the main setup phases and finishes with the created project root plus the next cmdlet to run.

## EXAMPLES

### EXAMPLE 1
Expand Down Expand Up @@ -150,6 +152,8 @@ Generated projects start with NovaModuleTools defaults for recursive discovery,
`-WhatIf` and
`-Confirm` support.

Press `Ctrl+C` during the interactive prompt flow if you want to cancel before Nova creates the scaffold.

## RELATED LINKS

- [Invoke-NovaBuild](./Invoke-NovaBuild.md)
Expand Down
14 changes: 7 additions & 7 deletions src/private/scaffold/InitializeNovaModuleScaffold.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ function Initialize-NovaModuleScaffold {
[switch]$Example
)

if (Test-Path $Paths.Project) {
Stop-NovaOperation -Message 'Project already exists, aborting' -ErrorId 'Nova.Workflow.ScaffoldProjectAlreadyExists' -Category ResourceExists -TargetObject $Paths.Project
if (Test-Path -LiteralPath $Paths.Project) {
Stop-NovaOperation -Message "Project folder already exists: $( $Paths.Project ). Choose a different project name or remove or move the existing folder before running Initialize-NovaModule again." -ErrorId 'Nova.Workflow.ScaffoldProjectAlreadyExists' -Category ResourceExists -TargetObject $Paths.Project
}

Write-Message "`nStarted Module Scaffolding" -color Green
Write-Message 'Setting up Directories'
Write-Message 'Starting Nova module scaffold' -color Green
Write-Message 'Creating project directories'

if ($Example) {
Initialize-NovaExampleModuleScaffold -Paths $Paths
Expand All @@ -21,7 +21,7 @@ function Initialize-NovaModuleScaffold {

if ($Answer.EnableGit -eq 'Yes') {
Update-NovaGitIgnore -ProjectRoot $Paths.Project -Confirm:$false
Write-Message 'Initialize Git Repo'
Write-Message 'Initializing Git repository'
New-InitiateGitRepo -DirectoryPath $Paths.Project
}
}
Expand All @@ -39,7 +39,7 @@ function Initialize-NovaDefaultModuleScaffold {
}

if ($Answer.EnablePester -eq 'Yes') {
Write-Message 'Include Pester Configs'
Write-Message 'Creating tests folder'
New-Item -ItemType Directory -Path $Paths.Tests | Out-Null
}
}
Expand All @@ -50,7 +50,7 @@ function Initialize-NovaExampleModuleScaffold {
[Parameter(Mandatory)][pscustomobject]$Paths
)

Write-Message 'Copy example project template'
Write-Message 'Copying packaged example project'
New-Item -ItemType Directory -Path $Paths.Project | Out-Null
Copy-NovaExampleProjectTemplate -DestinationPath $Paths.Project
}
71 changes: 66 additions & 5 deletions src/private/scaffold/InvokeNovaModuleInitializationWorkflow.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,72 @@ function Invoke-NovaModuleInitializationWorkflow {
[Parameter(Mandatory)][pscustomobject]$WorkflowContext
)

Initialize-NovaModuleScaffold -Answer $WorkflowContext.AnswerSet -Paths $WorkflowContext.Layout -Example:$WorkflowContext.Example
Write-NovaModuleProjectJson -Answer $WorkflowContext.AnswerSet -ProjectJsonFile $WorkflowContext.Layout.ProjectJsonFile -Example:$WorkflowContext.Example
if ($WorkflowContext.AnswerSet.EnableAgenticCopilot -eq 'Yes') {
Initialize-NovaModuleAgenticCopilotScaffold -Answer $WorkflowContext.AnswerSet -ProjectRoot $WorkflowContext.Layout.Project -Example:$WorkflowContext.Example
$progressActivity = 'Creating Nova module scaffold'

try {
Invoke-NovaModuleInitializationStep -Activity $progressActivity -Status 'Creating scaffold files' -PercentComplete 25 -Action {
Initialize-NovaModuleScaffold -Answer $WorkflowContext.AnswerSet -Paths $WorkflowContext.Layout -Example:$WorkflowContext.Example
}

Invoke-NovaModuleInitializationStep -Activity $progressActivity -Status 'Writing project.json' -PercentComplete 60 -Action {
Write-NovaModuleProjectJson -Answer $WorkflowContext.AnswerSet -ProjectJsonFile $WorkflowContext.Layout.ProjectJsonFile -Example:$WorkflowContext.Example
}

if ($WorkflowContext.AnswerSet.EnableAgenticCopilot -eq 'Yes') {
Invoke-NovaModuleInitializationStep -Activity $progressActivity -Status 'Applying Agentic Copilot starter' -PercentComplete 85 -Action {
Initialize-NovaModuleAgenticCopilotScaffold -Answer $WorkflowContext.AnswerSet -ProjectRoot $WorkflowContext.Layout.Project -Example:$WorkflowContext.Example
}
}
} finally {
Write-Progress -Activity $progressActivity -Completed
}

Write-NovaModuleInitializationResult -WorkflowContext $WorkflowContext
}

function Invoke-NovaModuleInitializationStep {
[CmdletBinding()]
param(
[Parameter(Mandatory)][string]$Activity,
[Parameter(Mandatory)][string]$Status,
[Parameter(Mandatory)][int]$PercentComplete,
[Parameter(Mandatory)][scriptblock]$Action
)

Write-Progress -Activity $Activity -Status $Status -PercentComplete $PercentComplete
& $Action
}

function Write-NovaModuleInitializationResult {
[CmdletBinding()]
param(
[Parameter(Mandatory)][pscustomobject]$WorkflowContext
)

Write-Message ("Created Nova module scaffold: $( $WorkflowContext.AnswerSet.ProjectName )") -color Green
Write-Message ("Project root: $( $WorkflowContext.Layout.Project )")

foreach ($line in (Get-NovaModuleInitializationNextStepLine -WorkflowContext $WorkflowContext)) {
Write-Message $line
}
}

function Get-NovaModuleInitializationNextStepLine {
[CmdletBinding()]
param(
[Parameter(Mandatory)][pscustomobject]$WorkflowContext
)

$nextSteps = @(
'Next steps:'
"Set-Location $( $WorkflowContext.Layout.Project )"
)

if ($WorkflowContext.Example) {
$nextSteps += 'Test-NovaBuild'
return $nextSteps
}

'Module {0} scaffolding complete' -f $WorkflowContext.AnswerSet.ProjectName | Write-Message -color Green
$nextSteps += 'Invoke-NovaBuild'
return $nextSteps
}
12 changes: 12 additions & 0 deletions tests/private/scaffold/InitializeNovaModuleScaffold.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,18 @@ BeforeAll {
}

Describe 'Initialize-NovaModuleScaffold' {
It 'fails with recovery guidance when the project folder already exists' {
$paths = [pscustomobject]@{Project = (Join-Path $TestDrive 'ExistingProject')}
$null = New-Item -ItemType Directory -Path $paths.Project -Force

{
Initialize-NovaModuleScaffold -Answer @{
EnableGit = 'No'
EnableAgenticCopilot = 'No'
} -Paths $paths
} | Should -Throw '*Project folder already exists:*Choose a different project name or remove or move the existing folder before running Initialize-NovaModule again.*'
}

It 'creates the default .gitignore for the standard scaffold when Git is enabled' {
$paths = Get-NovaModuleScaffoldLayout -Path $TestDrive -ProjectName 'StandardWithGit'
Mock Write-Message {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,29 @@ Describe 'Invoke-NovaModuleInitializationWorkflow' {
Mock Write-NovaModuleProjectJson {}
Mock Initialize-NovaModuleAgenticCopilotScaffold {}
Mock Write-Message {}
Mock Write-Progress {}

Invoke-NovaModuleInitializationWorkflow -WorkflowContext $script:context

Assert-MockCalled Initialize-NovaModuleScaffold -Times 1
Assert-MockCalled Write-NovaModuleProjectJson -Times 1
Assert-MockCalled Initialize-NovaModuleAgenticCopilotScaffold -Times 0
Assert-MockCalled Write-Message -Times 1
Assert-MockCalled Write-Progress -Times 3
Assert-MockCalled Write-Message -Times 4
Assert-MockCalled Write-Message -Times 1 -ParameterFilter {
$InputObject -eq 'Created Nova module scaffold: DemoModule' -and $color -eq 'Green'
}
Assert-MockCalled Write-Message -Times 1 -ParameterFilter {
$InputObject -eq 'Invoke-NovaBuild'
}
}

It 'invokes the Agentic Copilot scaffold step when the answer set requests it' {
Mock Initialize-NovaModuleScaffold {}
Mock Write-NovaModuleProjectJson {}
Mock Initialize-NovaModuleAgenticCopilotScaffold {}
Mock Write-Message {}
Mock Write-Progress {}

$contextWithAgentic = [pscustomobject]@{
AnswerSet = @{ProjectName = 'DemoModule'; EnableAgenticCopilot = 'Yes'}
Expand All @@ -42,5 +51,26 @@ Describe 'Invoke-NovaModuleInitializationWorkflow' {
Invoke-NovaModuleInitializationWorkflow -WorkflowContext $contextWithAgentic

Assert-MockCalled Initialize-NovaModuleAgenticCopilotScaffold -Times 1
Assert-MockCalled Write-Progress -Times 4
}

It 'suggests Test-NovaBuild as the next step for the example scaffold' {
Mock Initialize-NovaModuleScaffold {}
Mock Write-NovaModuleProjectJson {}
Mock Initialize-NovaModuleAgenticCopilotScaffold {}
Mock Write-Message {}
Mock Write-Progress {}

$exampleContext = [pscustomobject]@{
AnswerSet = @{ProjectName = 'DemoModule'; EnableAgenticCopilot = 'No'}
Layout = [pscustomobject]@{Project = '/tmp/DemoModule'; ProjectJsonFile = '/tmp/DemoModule/project.json'}
Example = $true
}

Invoke-NovaModuleInitializationWorkflow -WorkflowContext $exampleContext

Assert-MockCalled Write-Message -Times 1 -ParameterFilter {
$InputObject -eq 'Test-NovaBuild'
}
}
}
Loading