diff --git a/scripts/powershell/setup-plan.ps1 b/scripts/powershell/setup-plan.ps1 index e34de0fba8..e179f0d160 100644 --- a/scripts/powershell/setup-plan.ps1 +++ b/scripts/powershell/setup-plan.ps1 @@ -40,6 +40,13 @@ if (Test-Path $paths.IMPL_PLAN -PathType Leaf) { $content = [System.IO.File]::ReadAllText($template) $utf8NoBom = New-Object System.Text.UTF8Encoding($false) [System.IO.File]::WriteAllText($paths.IMPL_PLAN, $content, $utf8NoBom) + # Emit the copy status like the bash twin (setup-plan.sh); route to stderr + # in -Json mode so stdout stays pure JSON, matching the sibling messages. + if ($Json) { + [Console]::Error.WriteLine("Copied plan template to $($paths.IMPL_PLAN)") + } else { + Write-Output "Copied plan template to $($paths.IMPL_PLAN)" + } } else { Write-Warning "Plan template not found" # Create a basic plan file if template doesn't exist diff --git a/tests/test_setup_plan_no_overwrite.py b/tests/test_setup_plan_no_overwrite.py index c0db317263..ff19b1a0ac 100644 --- a/tests/test_setup_plan_no_overwrite.py +++ b/tests/test_setup_plan_no_overwrite.py @@ -224,3 +224,25 @@ def test_ps_setup_plan_preserves_existing_plan(plan_repo: Path) -> None: assert "IMPL_PLAN" in data # The skip message should be on stderr assert "already exists" in result.stderr + + +@pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available") +def test_ps_setup_plan_copied_message_on_stderr_in_json_mode(plan_repo: Path) -> None: + """First run in -Json mode must emit 'Copied plan template' on stderr (matching + the bash twin) while keeping stdout pure JSON. Before the fix the PowerShell + script emitted no copy status at all.""" + script = plan_repo / ".specify" / "scripts" / "powershell" / "setup-plan.ps1" + exe = "pwsh" if HAS_PWSH else _WINDOWS_POWERSHELL + result = subprocess.run( + [exe, "-NoProfile", "-File", str(script), "-Json"], + cwd=plan_repo, + capture_output=True, + text=True, + check=False, + env=_clean_env(), + ) + assert result.returncode == 0, result.stderr + # stdout stays parseable JSON; the status message goes to stderr. + data = json.loads(result.stdout) + assert "IMPL_PLAN" in data + assert "Copied plan template" in result.stderr