diff --git a/.github/workflows/lint-powershell.yml b/.github/workflows/lint-powershell.yml new file mode 100644 index 00000000..c12e1ada --- /dev/null +++ b/.github/workflows/lint-powershell.yml @@ -0,0 +1,124 @@ +name: PowerShell Install Scripts + +on: + push: + paths: + - 'install/powershell/*.ps1' + - '.github/workflows/lint-powershell.yml' + pull_request: + paths: + - 'install/powershell/*.ps1' + - '.github/workflows/lint-powershell.yml' + workflow_dispatch: + +jobs: + parse-windows-powershell-51: + name: Parse under Windows PowerShell 5.1 + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + + # The install scripts are fetched over HTTP and executed on stock Windows, + # where powershell.exe (5.1) is the default shell. 5.1 reads BOM-less files + # as ANSI (CP1252), so a non-ASCII glyph such as U+2713 decodes into a smart + # quote that silently terminates the enclosing string. The resulting parse + # errors point at unrelated lines far below the real cause, so parse every + # script with 5.1 explicitly rather than relying on PowerShell 7. + - name: Parse each script with 5.1 + shell: powershell + run: | + $ErrorActionPreference = 'Stop' + Write-Host "PowerShell $($PSVersionTable.PSVersion) / ANSI codepage $([System.Text.Encoding]::Default.WebName)" + + $failed = $false + Get-ChildItem 'install/powershell' -Filter *.ps1 | Sort-Object Name | ForEach-Object { + $errors = $null + [System.Management.Automation.Language.Parser]::ParseFile( + $_.FullName, [ref]$null, [ref]$errors) | Out-Null + + if ($errors -and $errors.Count -gt 0) { + $failed = $true + Write-Host "FAIL $($_.Name): $($errors.Count) parse error(s)" + $errors | Sort-Object { $_.Extent.StartLineNumber } | ForEach-Object { + Write-Host " L$($_.Extent.StartLineNumber): $($_.Message)" + } + } else { + Write-Host "OK $($_.Name)" + } + } + + if ($failed) { exit 1 } + + # A BOM would fix file-based parsing but break `irm ... | iex`: PowerShell 5.1 + # decodes an HTTP text/* body with no charset as ISO-8859-1, turning the BOM + # into literal "i>>?" text and failing with + # "The term 'i>>?$ErrorActionPreference' is not recognized". + # Keeping the sources pure ASCII is what makes both paths safe, so enforce it. + - name: Require pure ASCII and no BOM + shell: powershell + run: | + $ErrorActionPreference = 'Stop' + $failed = $false + + Get-ChildItem 'install/powershell' -Filter *.ps1 | Sort-Object Name | ForEach-Object { + $fileBad = $false + $bytes = [System.IO.File]::ReadAllBytes($_.FullName) + + if ($bytes.Length -ge 3 -and $bytes[0] -eq 0xEF -and $bytes[1] -eq 0xBB -and $bytes[2] -eq 0xBF) { + $failed = $true + $fileBad = $true + Write-Host "FAIL $($_.Name): has a UTF-8 BOM (breaks 'irm | iex'); remove it" + } + + # Report line/column of any byte outside ASCII so the fix is obvious. + $text = [System.Text.Encoding]::UTF8.GetString($bytes) + $lineNo = 0 + foreach ($line in ($text -split "`r?`n")) { + $lineNo++ + for ($i = 0; $i -lt $line.Length; $i++) { + if ([int]$line[$i] -gt 127) { + $failed = $true + $fileBad = $true + $cp = "U+{0:X4}" -f [int]$line[$i] + Write-Host "FAIL $($_.Name) L${lineNo} C$($i + 1): non-ASCII $cp" + Write-Host " build it from its code point instead, e.g. `$CHECK = [char]0x2713" + break + } + } + } + + if (-not $fileBad) { Write-Host "OK $($_.Name)" } + } + + if ($failed) { exit 1 } + + parse-powershell-7: + name: Parse under PowerShell 7 + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + + - name: Parse each script with 7 + shell: pwsh + run: | + $ErrorActionPreference = 'Stop' + Write-Host "PowerShell $($PSVersionTable.PSVersion)" + + $failed = $false + Get-ChildItem 'install/powershell' -Filter *.ps1 | Sort-Object Name | ForEach-Object { + $errors = $null + [System.Management.Automation.Language.Parser]::ParseFile( + $_.FullName, [ref]$null, [ref]$errors) | Out-Null + + if ($errors -and $errors.Count -gt 0) { + $failed = $true + Write-Host "FAIL $($_.Name): $($errors.Count) parse error(s)" + $errors | Sort-Object { $_.Extent.StartLineNumber } | ForEach-Object { + Write-Host " L$($_.Extent.StartLineNumber): $($_.Message)" + } + } else { + Write-Host "OK $($_.Name)" + } + } + + if ($failed) { exit 1 } diff --git a/install/powershell/examples.ps1 b/install/powershell/examples.ps1 index d4cb1b3d..7e64c85c 100644 --- a/install/powershell/examples.ps1 +++ b/install/powershell/examples.ps1 @@ -9,6 +9,12 @@ if (-not $env:GRAY) { $GRAY = "$([char]27)[38;2;128;128;128m" } els if (-not $env:BOLD) { $BOLD = "$([char]27)[1m" } else { $BOLD = $env:BOLD } if (-not $env:NC) { $NC = "$([char]27)[0m" } else { $NC = $env:NC } +# Symbols built from code points so this file stays pure ASCII. Windows PowerShell 5.1 +# reads BOM-less files as ANSI (CP1252), where a literal U+2713 decodes to a smart quote +# that silently terminates the enclosing string and breaks parsing. +$CHECK = [char]0x2713 +$CROSS = [char]0x2717 + # Examples configuration $EXAMPLES_FOLDER_NAME = "plainlang-examples" $EXAMPLES_DOWNLOAD_URL = "https://codeplain.ai/examples/windows" @@ -40,7 +46,7 @@ if (-not (Test-Path $EXTRACT_PATH -PathType Container)) { try { New-Item -ItemType Directory -Path $EXTRACT_PATH -Force | Out-Null } catch { - Write-Host " ${RED}✗ Failed to create directory: ${EXTRACT_PATH}${NC}" + Write-Host " ${RED}${CROSS} Failed to create directory: ${EXTRACT_PATH}${NC}" Write-Host " ${GRAY}Skipping example download.${NC}" $SKIP_DOWNLOAD = $true } @@ -73,7 +79,7 @@ if (-not $SKIP_DOWNLOAD) { Clear-Host Write-Host "" - Write-Host " ${GREEN}✓ Examples downloaded successfully!${NC}" + Write-Host " ${GREEN}${CHECK} Examples downloaded successfully!${NC}" Write-Host "" Write-Host " ${GRAY}Examples are in: ${EXTRACTED_DIR}${NC}" Write-Host "" @@ -86,15 +92,15 @@ if (-not $SKIP_DOWNLOAD) { Write-Host " ${GRAY}See hello-world/python/README.md for details.${NC}" Write-Host "" } catch { - Write-Host " ${RED}✗ Failed to extract examples.${NC}" + Write-Host " ${RED}${CROSS} Failed to extract examples.${NC}" } Remove-Item -Path $TEMP_ZIP -Force -ErrorAction SilentlyContinue } else { - Write-Host " ${RED}✗ Failed to download examples.${NC}" + Write-Host " ${RED}${CROSS} Failed to download examples.${NC}" } } catch { - Write-Host " ${RED}✗ Failed to download examples.${NC}" + Write-Host " ${RED}${CROSS} Failed to download examples.${NC}" Remove-Item -Path $TEMP_ZIP -Force -ErrorAction SilentlyContinue } diff --git a/install/powershell/install.ps1 b/install/powershell/install.ps1 index 0ac18e80..81daf7db 100644 --- a/install/powershell/install.ps1 +++ b/install/powershell/install.ps1 @@ -32,6 +32,12 @@ $GRAY_LIGHT = "$ESC[38;2;211;211;211m" # #D3D3D3 $BOLD = "$ESC[1m" $NC = "$ESC[0m" # No Color / Reset +# Symbols built from code points so this file stays pure ASCII. Windows PowerShell 5.1 +# reads BOM-less files as ANSI (CP1252), where a literal U+2713 decodes to a smart quote +# that silently terminates the enclosing string and breaks parsing. +$CHECK = [char]0x2713 +$ROCKET = [System.Char]::ConvertFromUtf32(0x1F680) + # Export colors for child scripts (as environment variables) $env:YELLOW = $YELLOW $env:GREEN = $GREEN @@ -92,11 +98,11 @@ function Test-ApiKey { if (-not (Get-Command uv -ErrorAction SilentlyContinue)) { Write-Host "${GRAY}uv is not installed.${NC}" Install-Uv - Write-Host "${GREEN}✓${NC} uv installed successfully" + Write-Host "${GREEN}${CHECK}${NC} uv installed successfully" Write-Host "" } -Write-Host "${GREEN}✓${NC} uv detected" +Write-Host "${GREEN}${CHECK}${NC} uv detected" Write-Host "" try { @@ -125,16 +131,16 @@ if ($codeplainLine) { $newLine = @(uv tool list 2>$null) | Where-Object { $_ -match '^codeplain' } | Select-Object -First 1 $newVersion = ($newLine -replace 'codeplain v', '').Trim() if ($currentVersion -eq $newVersion) { - Write-Host "${GREEN}✓${NC} codeplain is already up to date (${newVersion})" + Write-Host "${GREEN}${CHECK}${NC} codeplain is already up to date (${newVersion})" } else { - Write-Host "${GREEN}✓${NC} codeplain upgraded from ${currentVersion} to ${newVersion}!" + Write-Host "${GREEN}${CHECK}${NC} codeplain upgraded from ${currentVersion} to ${newVersion}!" } } else { Write-Host "Installing codeplain...${NC}" Write-Host "" uv tool install codeplain if (-not $nonInteractive) { Clear-Host } - Write-Host "${GREEN}✓ codeplain installed successfully!${NC}" + Write-Host "${GREEN}${CHECK} codeplain installed successfully!${NC}" } # Ensure uv tool bin directory is on user PATH permanently (so codeplain is available) @@ -146,12 +152,12 @@ if ($userPath) { $newPath = ($userPath.TrimEnd(';') + ';' + $uvBinDir) [Environment]::SetEnvironmentVariable('Path', $newPath, 'User') $env:Path = $uvBinDir + ';' + $env:Path - Write-Host "${GREEN}✓${NC} added $uvBinDir to your user PATH" + Write-Host "${GREEN}${CHECK}${NC} added $uvBinDir to your user PATH" } } else { [Environment]::SetEnvironmentVariable('Path', $uvBinDir, 'User') $env:Path = $uvBinDir + ';' + $env:Path - Write-Host "${GREEN}✓${NC} added $uvBinDir to your user PATH" + Write-Host "${GREEN}${CHECK}${NC} added $uvBinDir to your user PATH" } Write-Host "" @@ -181,7 +187,7 @@ if ($env:CODEPLAIN_API_KEY) { Write-Host "${GRAY}Verifying your existing API key...${NC}" $existingResult = Test-ApiKey $env:CODEPLAIN_API_KEY if ($existingResult -eq "valid") { - Write-Host "${GREEN}✓${NC} Using existing API key." + Write-Host "${GREEN}${CHECK}${NC} Using existing API key." $skipApiKeySetup = $true $apiKeyVerified = $true } elseif ($existingResult -eq "invalid") { @@ -225,7 +231,7 @@ if (-not $skipApiKeySetup) { Write-Host "${GRAY}Verifying your API key...${NC}" $result = Test-ApiKey $apiKey if ($result -eq "valid") { - Write-Host "${GREEN}✓${NC} API key verified." + Write-Host "${GREEN}${CHECK}${NC} API key verified." Write-Host "" $apiKeyVerified = $true break @@ -252,7 +258,7 @@ if ($skipApiKeySetup) { # Persist as user environment variable (survives reboots) [Environment]::SetEnvironmentVariable('CODEPLAIN_API_KEY', $apiKey, 'User') - Write-Host "${GREEN}✓ API key saved to user environment variables${NC}" + Write-Host "${GREEN}${CHECK} API key saved to user environment variables${NC}" } # ASCII Art Welcome @@ -268,7 +274,7 @@ Write-Host @' Write-Host "" # Only claim success when a verified API key is actually configured. if ($apiKeyVerified) { - Write-Host "${GREEN}✓ Sign in successful.${NC}" + Write-Host "${GREEN}${CHECK} Sign in successful.${NC}" Write-Host "" } Write-Host " ${WHITE}Welcome to *codeplain!${NC}" @@ -392,7 +398,7 @@ $plynInstalled = $false if ($editorCmds.Count -gt 0 -and $installPlyn -notmatch '^[Nn]$') { foreach ($editor in $editorCmds) { & $editor.Cmd --install-extension Codeplain.plyn - Write-Host "${GREEN}✓${NC} plyn installed for $($editor.Name)" + Write-Host "${GREEN}${CHECK}${NC} plyn installed for $($editor.Name)" } $plynInstalled = $true Write-Host "" @@ -435,7 +441,7 @@ if ($env:CODEPLAIN_API_KEY) { $verifyOk = $false } if ($verifyOk) { - Write-Host "${GREEN}✓${NC} Installation verified." + Write-Host "${GREEN}${CHECK}${NC} Installation verified." } else { Write-Host "${RED}Something went wrong during installation.${NC}" Write-Host "${GRAY}Output of 'codeplain --status':${NC}" @@ -473,7 +479,7 @@ Write-Host " ${WHITE}${BOLD}codeplain your-project.plain${NC}" Write-Host "" Write-Host " ${GRAY}Discord: https://discord.gg/cgbynb9hFq Docs: https://plainlang.org/${NC}" Write-Host "" -Write-Host " ${GRAY}Happy development!${NC} 🚀" +Write-Host " ${GRAY}Happy development!${NC} ${ROCKET}" Write-Host "" # Refresh environment for this session diff --git a/install/powershell/walkthrough.ps1 b/install/powershell/walkthrough.ps1 index 2fcbe93e..a8216958 100644 --- a/install/powershell/walkthrough.ps1 +++ b/install/powershell/walkthrough.ps1 @@ -8,6 +8,19 @@ if (-not $env:GRAY) { $GRAY = "$([char]27)[38;2;128;128;128m" } els if (-not $env:BOLD) { $BOLD = "$([char]27)[1m" } else { $BOLD = $env:BOLD } if (-not $env:NC) { $NC = "$([char]27)[0m" } else { $NC = $env:NC } +# Box-drawing and symbol characters built from code points so this file stays pure ASCII. +# Windows PowerShell 5.1 reads BOM-less files as ANSI (CP1252), where these glyphs decode +# to smart quotes that silently terminate the enclosing string and break parsing. +$TRI = [char]0x25B2 +$TL = [char]0x250C # top-left corner +$TR = [char]0x2510 # top-right corner +$BL = [char]0x2514 # bottom-left corner +$BR = [char]0x2518 # bottom-right corner +$V = [char]0x2502 # vertical line +$HR = ([string][char]0x2500) * 56 # horizontal rule +$BOX_TOP = "${TL}${HR}${TR}" +$BOX_BOTTOM = "${BL}${HR}${BR}" + # Onboarding Step 1: Introduction to Plain Clear-Host Write-Host "" @@ -19,14 +32,14 @@ Write-Host " Write specs in natural language extended with additional syntax ba Write-Host "" Write-Host " A ***plain file has these key sections:" Write-Host "" -Write-Host "${GRAY} ┌────────────────────────────────────────────────────────┐${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GREEN}***definitions***${NC} - key concepts in your app ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GREEN}***implementation reqs***${NC} - implementation details ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GREEN}***test reqs***${NC} - testing requirements ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GREEN}***functional specs***${NC} - what the app should do ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} └────────────────────────────────────────────────────────┘${NC}" +Write-Host "${GRAY} ${BOX_TOP}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GREEN}***definitions***${NC} - key concepts in your app ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GREEN}***implementation reqs***${NC} - implementation details ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GREEN}***test reqs***${NC} - testing requirements ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GREEN}***functional specs***${NC} - what the app should do ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${BOX_BOTTOM}${NC}" Write-Host "" Write-Host " Let's see each section in a `"hello, world`" example." Write-Host "" @@ -42,27 +55,27 @@ Write-Host "" Write-Host " Define ${WHITE}${BOLD}reusable concepts${NC} using the ${WHITE}${BOLD}:Concept:${NC} notation." Write-Host " These become building blocks you can reference anywhere." Write-Host "" -Write-Host "${GRAY} ┌────────────────────────────────────────────────────────┐${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GREEN}${BOLD}***definitions***${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${WHITE}${BOLD}- :App: is a console application.${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}***implementation reqs***${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}- :Implementation: should be in Python.${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}***test reqs***${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}- :ConformanceTests: should use pytest.${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}***functional specs***${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}- :App: should display `"hello, world`".${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} └────────────────────────────────────────────────────────┘${NC}" -Write-Host "" -Write-Host " ${GREEN}▲${NC} The ${WHITE}:App:${NC} concept is defined once and used throughout the specs." +Write-Host "${GRAY} ${BOX_TOP}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GREEN}${BOLD}***definitions***${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${WHITE}${BOLD}- :App: is a console application.${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}***implementation reqs***${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}- :Implementation: should be in Python.${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}***test reqs***${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}- :ConformanceTests: should use pytest.${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}***functional specs***${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}- :App: should display `"hello, world`".${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${BOX_BOTTOM}${NC}" +Write-Host "" +Write-Host " ${GREEN}${TRI}${NC} The ${WHITE}:App:${NC} concept is defined once and used throughout the specs." Write-Host " Concepts help keep your specs consistent and clear." Write-Host "" Read-Host " Press ${WHITE}[Enter]${NC} to continue..." @@ -77,27 +90,27 @@ Write-Host "" Write-Host " Specify ${WHITE}${BOLD}implementation details${NC} and ${WHITE}${BOLD}testing requirements${NC}." Write-Host " This guides how the code should be generated and verified." Write-Host "" -Write-Host "${GRAY} ┌────────────────────────────────────────────────────────┐${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}***definitions***${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}- :App: is a console application.${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GREEN}${BOLD}***implementation reqs***${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${WHITE}${BOLD}- :Implementation: should be in Python.${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GREEN}${BOLD}***test reqs***${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${WHITE}${BOLD}- :ConformanceTests: should use pytest.${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}***functional specs***${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}- :App: should display `"hello, world`".${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} └────────────────────────────────────────────────────────┘${NC}" -Write-Host "" -Write-Host " ${GREEN}▲${NC} ${WHITE}${BOLD}Implementation reqs${NC} define the language and frameworks." +Write-Host "${GRAY} ${BOX_TOP}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}***definitions***${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}- :App: is a console application.${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GREEN}${BOLD}***implementation reqs***${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${WHITE}${BOLD}- :Implementation: should be in Python.${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GREEN}${BOLD}***test reqs***${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${WHITE}${BOLD}- :ConformanceTests: should use pytest.${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}***functional specs***${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}- :App: should display `"hello, world`".${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${BOX_BOTTOM}${NC}" +Write-Host "" +Write-Host " ${GREEN}${TRI}${NC} ${WHITE}${BOLD}Implementation reqs${NC} define the language and frameworks." Write-Host " ${WHITE}${BOLD}Test reqs${NC} ensure the generated code is verified." Write-Host "" Read-Host " Press ${WHITE}[Enter]${NC} to continue..." @@ -112,27 +125,27 @@ Write-Host "" Write-Host " This is where you describe ${WHITE}what your app should do${NC}," Write-Host " written in natural language. No code, just requirements." Write-Host "" -Write-Host "${GRAY} ┌────────────────────────────────────────────────────────┐${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}***definitions***${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}- :App: is a console application.${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}***implementation reqs***${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}- :Implementation: should be in Python.${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}***test reqs***${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}- :ConformanceTests: should use pytest.${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GREEN}${BOLD}***functional specs***${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${WHITE}${BOLD}- :App: should display `"hello, world`".${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} │${NC} ${GRAY}│${NC}" -Write-Host "${GRAY} └────────────────────────────────────────────────────────┘${NC}" -Write-Host "" -Write-Host " ${GREEN}▲${NC} The ${WHITE}${BOLD}functional spec${NC} describes ${WHITE}${BOLD}what the app does${NC}." +Write-Host "${GRAY} ${BOX_TOP}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}***definitions***${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}- :App: is a console application.${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}***implementation reqs***${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}- :Implementation: should be in Python.${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}***test reqs***${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}- :ConformanceTests: should use pytest.${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GREEN}${BOLD}***functional specs***${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${WHITE}${BOLD}- :App: should display `"hello, world`".${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${V}${NC} ${GRAY}${V}${NC}" +Write-Host "${GRAY} ${BOX_BOTTOM}${NC}" +Write-Host "" +Write-Host " ${GREEN}${TRI}${NC} The ${WHITE}${BOLD}functional spec${NC} describes ${WHITE}${BOLD}what the app does${NC}." Write-Host " Here, it simply displays `"hello, world`"." Write-Host "" Read-Host " Press ${WHITE}[Enter]${NC} to continue..."