How PowerShell Script Generation Differs from Bash Scripts Using the `--script ps` Flag

When you pass --script ps to specify init, the CLI downloads a PowerShell-based project template instead of the default Bash scripts, replacing POSIX shell syntax with PowerShell cmdlets, advanced parameter validation, and Windows-native error handling while preserving identical agent-context update functionality.

The github/spec-kit repository provides an AI assistant context management toolkit that supports multiple scripting environments. When initializing a new project, the --script flag controls whether the generated scaffolding uses Bash or PowerShell for its automation scripts. Understanding how PowerShell script generation differs from the default Bash implementation ensures you select the correct tooling for your operating system and maintenance preferences.

Template Selection Logic in the CLI

In src/specify_cli/__init__.py, the download_template_from_github function constructs an asset name pattern that determines which template bundle is fetched from the release assets. The function builds the pattern as follows:

pattern = f"spec-kit-template-{ai_assistant}-{script_type}"

When you invoke specify init --script ps, the script_type variable receives the value "ps". This triggers the download of the PowerShell-specific asset bundle, which unpacks to reveal scripts/powershell/update-agent-context.ps1 and its companion files instead of the default scripts/bash/update-agent-context.sh.

Syntax and Structural Comparison

The two implementations share identical functional goals—parsing plan.md and updating agent context files—but diverge significantly in syntax and execution model.

Shebang and Execution Environment

Bash (sh default): Uses #!/usr/bin/env bash and relies on POSIX shell compatibility across Unix-like systems.

PowerShell (ps): Uses #!/usr/bin/env pwsh and requires PowerShell 7+ (pwsh) for cross-platform execution or Windows PowerShell for Windows-specific environments.

Parameter Declaration and Validation

Bash handles inputs through positional parameters and manual validation logic:

#!/usr/bin/env bash
set -e
SCRIPT_DIR="$(CDPATH="" cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

PowerShell provides declarative parameter blocks with type validation and enumerated value constraints:

#!/usr/bin/env pwsh
param(
    [ValidateSet('claude','gemini','copilot','cursor-agent','qwen','opencode','codex','windsurf','kilocode','auggie','roo','codebuddy','amp','shai','kiro-cli','agy','bob','qodercli','generic')]
    [string]$AgentType
)
$ErrorActionPreference = 'Stop'

Functional Implementation Differences

Logging and Output Methods

Bash implements custom logging functions using echo and Unicode characters:

log_info() { echo "INFO: $1"; }
log_success() { echo "✓ $1"; }
log_warning() { echo "WARNING: $1"; }

PowerShell leverages native cmdlets and host output methods:

function Write-Info { param([string]$Message) Write-Host "INFO: $Message" }
function Write-Success { param([string]$Message) Write-Host "$([char]0x2713) $Message" }
function Write-WarningMsg { param([string]$Message) Write-Warning $Message }

Error Handling Strategies

Bash relies on strict mode flags and process traps:

  • set -e terminates execution on any command failure
  • set -u treats unset variables as errors
  • trap cleanup EXIT ensures resource removal via rm calls

PowerShell uses preference variables and explicit flow control:

  • $ErrorActionPreference = 'Stop' converts non-terminating errors into terminating exceptions
  • Functions call exit 1 immediately on validation failures
  • Automatic session cleanup or explicit Remove-Item handles temporary objects

String Manipulation and Path Resolution

Bash depends on external utilities and shell parameter expansion:

  • grep and sed for text extraction and substitution
  • Complex subshell syntax for path resolution: SCRIPT_DIR="$(CDPATH="" cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

PowerShell uses object-oriented methods and built-in cmdlets:

  • Regex operators: -match and [Regex]::Escape()
  • String methods: .Trim()
  • Cross-platform path joining: Join-Path $ScriptDir 'common.ps1'
  • Dot-sourcing imports: . (Join-Path $ScriptDir 'common.ps1')

Shared Helper Architecture

Both scripts modularize functionality through shared helper files to avoid code duplication. The Bash version sources scripts/bash/common.sh for utilities like get_feature_paths, while the PowerShell version imports scripts/powershell/common.ps1 containing the Get-FeaturePathsEnv function. These helpers maintain parity in feature detection and environment variable management while respecting their respective language idioms and scope rules.

Summary

  • The --script ps flag sets script_type to "ps" in src/specify_cli/__init__.py, altering the asset name pattern to fetch the PowerShell template bundle instead of the default Bash scaffold.
  • Bash scripts use POSIX syntax, positional parameters, external utilities (grep, sed), and strict-mode error handling (set -e, set -u).
  • PowerShell scripts provide advanced parameter validation via [ValidateSet()], use cmdlet-based logging (Write-Host), and implement error handling through $ErrorActionPreference = 'Stop'.
  • File extensions differ: .sh for Bash versus .ps1 for PowerShell, with corresponding shebang lines (#!/usr/bin/env bash vs #!/usr/bin/env pwsh).
  • Both implementations preserve identical business logic—parsing plan.md and updating agent contexts—but adapt their implementation to platform-native conventions and type systems.

Frequently Asked Questions

Can I use PowerShell scripts on Linux or macOS?

Yes. The generated update-agent-context.ps1 uses #!/usr/bin/env pwsh as its shebang, making it compatible with PowerShell 7+ (pwsh) across Linux, macOS, and Windows. You must install PowerShell Core on non-Windows systems to execute the scripts natively.

Does the PowerShell version support the same AI assistants as the Bash version?

Yes. Both scripts support the identical range of AI assistants including Claude, Gemini, Copilot, Cursor Agent, Qwen, and others. The PowerShell version enforces valid inputs through the [ValidateSet()] attribute on the $AgentType parameter, providing compile-time style validation that the Bash version implements through manual conditional checks.

How does the CLI determine which template asset to download?

In src/specify_cli/__init__.py, the download_template_from_github function constructs the asset name using the pattern spec-kit-template-{ai_assistant}-{script_type}. When script_type equals "ps", the CLI fetches the release asset containing the PowerShell-specific directory structure (scripts/powershell/) rather than the default Bash structure (scripts/bash/).

Which error handling approach is more robust for CI/CD pipelines?

PowerShell's $ErrorActionPreference = 'Stop' provides immediate, deterministic failure on any error, integrating cleanly with CI/CD systems that monitor exit codes. Bash's set -e offers similar behavior but can behave unexpectedly in complex pipelines or with certain built-in commands. For Windows-centric pipelines, PowerShell provides better integration with Azure DevOps and GitHub Actions Windows runners, while Bash remains preferable for Linux-native workflows.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →