Skip to content

Add Windows support via Git Bash with PowerShell fallback#122

Merged
max-sixty merged 15 commits intomainfrom
windows
Dec 10, 2025
Merged

Add Windows support via Git Bash with PowerShell fallback#122
max-sixty merged 15 commits intomainfrom
windows

Conversation

@max-sixty
Copy link
Owner

Summary

  • Implements cross-platform shell execution for Windows
  • Prefers Git Bash when available, falls back to PowerShell
  • Adds PowerShell shell integration support

Changes

  • New shell_exec module providing unified shell abstraction
  • Updated shell execution points to use ShellConfig
  • Added Shell::PowerShell variant with config paths and template

Design

Git Bash enables same bash hook syntax on all platforms. PowerShell fallback works for basic commands but has documented limitations (no directory change, hooks using bash syntax won't work).

Closes #121

🤖 Generated with Claude Code

max-sixty and others added 15 commits December 10, 2025 03:31
Implements cross-platform shell execution for Windows:

- New `shell_exec` module providing unified shell abstraction
  - Unix: uses `sh -c`
  - Windows: prefers Git Bash (checks $MSYSTEM, standard paths,
    derives from git.exe), falls back to PowerShell
  - Cached detection via OnceLock for performance

- Updated shell execution points to use ShellConfig:
  - execute_streaming() in output/handlers.rs
  - spawn_detached_windows() in commands/process.rs
  - InteractiveOutput::execute() in output/interactive.rs
  - help_pager.rs (skips less default without POSIX shell)

- Added PowerShell shell integration:
  - New Shell::PowerShell variant with config paths
  - templates/powershell.ps1 with documented limitations
  - Basic wt wrapper and tab completion support

Design: Git Bash enables same bash hook syntax on all platforms.
PowerShell fallback works for basic commands but has limitations
(no directory change, hooks using bash syntax won't work).

Closes #121

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Use `which` crate for cleaner executable path detection
- Add Windows-specific unit tests for CI:
  - Shell detection (Git Bash vs PowerShell)
  - Command execution validation
  - POSIX syntax support verification
- Add documentation about Windows limitations

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Collapse nested if statement in find_git_bash() to satisfy clippy
- Update home directory error message to mention Windows environment variable

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Gate Command import with #[cfg(unix)] in process.rs
- Gate Stdio and ShellConfig imports with #[cfg(unix)] in interactive.rs
- Add PowerShell profile detection to is_integration_configured()
- Fix collapsible_if warnings

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Previously we had a custom Register-ArgumentCompleter that didn't
properly interface with clap's completion system. Now we let clap
generate its completion script and eval it, which handles:
- Proper cursor position tracking
- Tab-separated output format with help text
- Correct argument passing with -- separator

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
When installing PowerShell shell integration on Windows without Git Bash:
- Show warning about limited functionality
- List specific limitations (no directory change, bash hooks won't work)
- Suggest installing Git for Windows

Also:
- Include PowerShell in auto-detected shells on Windows
- Add is_windows_without_git_bash() helper to ShellConfig

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Check stdout contains expected output instead of just "not empty"
- Remove fallback assertions that would pass even if wrong stream used
- Simplify POSIX redirection test command

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
PowerShell doesn't support redirecting stdout to stderr (*>&2 fails).
Document this known limitation with link to PowerShell issue #7620.

The practical impact is limited since:
- Users are warned at shell install time about PowerShell limitations
- Hooks using bash syntax need Git Bash anyway
- Most hook output is informational

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
PowerShell can now change directories after `wt switch` using the same
pattern as zoxide/fnm/starship:

1. `--internal` now takes an optional shell type: `--internal=posix` (default)
   or `--internal=powershell`
2. PowerShell wrapper passes `--internal=powershell` and captures stdout
3. Binary outputs `Set-Location 'path'` for PowerShell, `cd 'path'` for POSIX
4. Wrapper executes the directive via Invoke-Expression

Changes:
- Add DirectiveShell enum (Posix, Powershell) to cli.rs
- Update OutputMode::Directive to carry shell type
- Add shell-specific path escaping in directive.rs
- Rewrite PowerShell template to use directive mode
- Update install warning (only hooks limitation now, not cd)
- Add tests for PowerShell path formatting

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Fix PowerShell backtick escaping (backticks are literal in single quotes)
- Sanitize Windows-illegal characters in log filenames (< > : " \ | ? *)
- Use dirs crate for non-English Windows Documents folder
- Support both PowerShell Core and Windows PowerShell 5.1 profiles
- Add require_equals to --internal flag to fix CLI parsing
- Fix PowerShell wrapper stderr handling (don't merge with stdout)
- Redirect hook stdout to stderr on non-POSIX shells via Stdio::from(io::stderr())

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Handle Windows reserved device names (CON, PRN, AUX, NUL, COM1-9, LPT1-9)
  in filename sanitization by prefixing with underscore
- Propagate exit code in PowerShell wrapper so $? and $LASTEXITCODE are
  consistent for scripts and CI

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Remove Unix-only restriction from test common module
- Add shell_available() function to detect available shells at runtime
- Add skip_if_shell_unavailable! macro to skip tests for unavailable shells
- Enable shell-integration-tests feature on Windows CI
- Tests automatically skip unavailable shells (zsh, fish on Windows)

This allows bash tests to run on Windows via Git Bash while gracefully
skipping zsh/fish tests that require those shells.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Code review found that COM0 and LPT0 were incorrectly being treated as
reserved device names. Only COM1-9 and LPT1-9 are reserved on Windows,
not COM0/LPT0.

Changes:
- Change `is_ascii_digit()` to `matches!(c, '1'..='9')` in sanitize_for_filename
- Add comprehensive test coverage for reserved device names
- Add explanatory comment for Windows process detachment

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
The shell integration tests and progressive output tests use Unix-specific
features (PTY, dev-detach binary with setsid). Gate these modules with
#[cfg(unix)] so they don't compile on Windows, fixing dead code warnings.

Changes:
- Gate shell module with #[cfg(all(unix, feature = "shell-integration-tests"))]
- Gate progressive_output module with #[cfg(unix)]
- Gate e2e_shell.rs, e2e_shell_post_start.rs, shell_wrapper.rs with #[cfg(all(unix, ...))]
- Gate list_progressive.rs with #[cfg(unix)]

Windows shell integration support is planned for a future PR.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
The tests/common/mod.rs module is only used by integration tests which
are gated to Unix-only. On Windows, the common module was being compiled
but had no consumers, causing dead code warnings that failed CI.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@max-sixty max-sixty merged commit 6922780 into main Dec 10, 2025
16 checks passed
@max-sixty max-sixty deleted the windows branch December 10, 2025 11:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Windows Support

1 participant