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

> Discover how PowerShell script generation with the --script ps flag replaces Bash scripts with advanced PowerShell cmdlets, Windows-native error handling and parameter validation for your spec-kit projects.

- Repository: [GitHub/spec-kit](https://github.com/github/spec-kit)
- Tags: how-to-guide
- Published: 2026-03-05

---

**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`](https://github.com/github/spec-kit/blob/main/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:

```python
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`](https://github.com/github/spec-kit/blob/main/scripts/bash/update-agent-context.sh).

## Syntax and Structural Comparison

The two implementations share identical functional goals—parsing [`plan.md`](https://github.com/github/spec-kit/blob/main/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:

```bash
#!/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:

```powershell
#!/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:

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

```

**PowerShell** leverages native cmdlets and host output methods:

```powershell
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`](https://github.com/github/spec-kit/blob/main/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`](https://github.com/github/spec-kit/blob/main/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`](https://github.com/github/spec-kit/blob/main/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`](https://github.com/github/spec-kit/blob/main/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.