Public/Common/New-AutomationWorkspace.ps1

function New-AutomationWorkspace {
    <#
    .SYNOPSIS
    Scaffolds a customer workspace from the templates shipped inside this module.
 
    .DESCRIPTION
    Creates the client repo shape - init.ps1, workspace.json, CLAUDE.md, the data,
    exports, migrations, output and secrets folders, and .gitignore - from the module's
    own Templates\customer-repo folder.
 
    Files are SEEDS: each one is copied only if it does not already exist, so re-running
    never overwrites work. The exception is the framework-owned set that the client's
    init.ps1 refreshes from this same template folder on every session (see $managedFiles
    there); those are seeded here and then kept current from the module.
 
    bootstrap.ps1 calls this after cloning and importing the module, so that the module
    is the single source of every scaffold - bootstrap itself knows nothing about
    templates, and this command works identically from a client workspace's .system\ copy.
 
    .PARAMETER Path
    Target workspace root. Defaults to the current directory.
 
    .PARAMETER InitialiseGit
    Run 'git init' in the target when it is not already a repository.
 
    .EXAMPLE
    New-AutomationWorkspace -Path 'C:\source\repos\NKDAClient-Contoso' -InitialiseGit
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param(
        [string]$Path = (Get-Location).Path,
        [switch]$InitialiseGit
    )

    $ErrorActionPreference = 'Stop'

    $templateRoot = Join-Path $script:ModuleRoot 'Templates\customer-repo'
    if (-not (Test-Path -LiteralPath $templateRoot)) {
        throw "Customer-repo template not found at '$templateRoot'. The module copy looks incomplete."
    }

    New-Item -Path $Path -ItemType Directory -Force | Out-Null
    $Path = (Resolve-Path -LiteralPath $Path).Path
    Write-FixSection "Scaffolding customer workspace in $Path"

    # A real dot-file in the template folder would hide the template from the tools repo,
    # so it ships under a neutral name and is renamed on the way out.
    $renameMap = @{ 'gitignore.template' = '.gitignore' }

    # Source material for the managed block that init.ps1 writes into the workspace's
    # CLAUDE.md. It stays in the module - copying it into the workspace would put a
    # second, immediately-stale copy of the same guidance in the customer's repo.
    $doNotScaffold = @('CLAUDE.managed.md', '.managed')

    $templateRootLength = (Get-Item -LiteralPath $templateRoot).FullName.Length + 1
    foreach ($template in (Get-ChildItem -LiteralPath $templateRoot -Recurse -File -Force)) {
        $relative = $template.FullName.Substring($templateRootLength)
        if ($relative -in $doNotScaffold) { continue }
        if ($renameMap.ContainsKey($relative)) { $relative = $renameMap[$relative] }
        $destination = Join-Path $Path $relative

        if (Test-Path -LiteralPath $destination) {
            Write-Host " Skipped $relative (already exists)" -ForegroundColor DarkGray
            continue
        }
        if (-not $PSCmdlet.ShouldProcess($destination, 'Create from template')) { continue }
        New-Item -Path (Split-Path -Parent $destination) -ItemType Directory -Force | Out-Null
        Copy-Item -LiteralPath $template.FullName -Destination $destination
        # When this module is itself running from a workspace's read-only .system\ copy,
        # Copy-Item carries the attribute across; the scaffolded file is the workspace's own.
        (Get-Item -LiteralPath $destination).IsReadOnly = $false
        Write-Host " Created $relative" -ForegroundColor Green
    }

    foreach ($folder in 'data', 'exports', 'migrations', 'output', 'secrets') {
        New-Item -Path (Join-Path $Path $folder) -ItemType Directory -Force | Out-Null
    }

    if ($InitialiseGit -and -not (Test-Path -LiteralPath (Join-Path $Path '.git'))) {
        if ($PSCmdlet.ShouldProcess($Path, 'git init')) {
            Write-FixStep 'Initialising git repository'
            git -C $Path init | Out-Null
            if ($LASTEXITCODE -ne 0) { Write-Warning 'git init failed; initialise the repository by hand.' }
        }
    }

    return Get-Item -LiteralPath $Path
}