Skip to content

feat: add Windows/PowerShell support#21

Open
baltika10 wants to merge 1 commit into
VincentHardouin:mainfrom
baltika10:feat/powershell-support
Open

feat: add Windows/PowerShell support#21
baltika10 wants to merge 1 commit into
VincentHardouin:mainfrom
baltika10:feat/powershell-support

Conversation

@baltika10

Copy link
Copy Markdown

Fixes #20.

What

Adds Windows/PowerShell support to the snip plugin. On Windows, opencode drives PowerShell, not bash. The plugin previously assumed a POSIX shell, which caused it to:

  1. Wrap $env:X='Y' assignments with snip, silently dropping all non-interactive environment guards
  2. Attempt to exec PowerShell cmdlets (Write-Output, Get-ChildItem, etc.) as binaries, which fails
  3. Miss commands after a cd when separated by a newline (not ;/&&)

Changes

src/index.ts

  • POWERSHELL_SKIP_RE: skip segments starting with $, @, &, { — these are PowerShell variable assignments, here-strings, call operators, and script blocks that can never be external commands
  • POWERSHELL_CMDLET_RE: skip Verb-Noun cmdlets (Write-Output, Get-ChildItem, Set-Location, etc.) — these are shell builtins that snip cannot exec
  • Newline in OPERATOR_RE: split on \r?\n in addition to ;, &&, ||, & — so commands on separate lines are snipped independently
  • snipSegment helper: extracts per-segment pipe handling from toolExecuteBefore so that pipe detection works correctly with newline-separated multi-line commands

src/index.test.ts

  • 9 tests for PowerShell patterns (env vars, cmdlets, call operator, chained commands)
  • 5 tests for newline splitting (basic, with unproxyable commands, with operators, with pipes, with PowerShell prelude)

Backward compatibility

  • All 30 existing tests pass unchanged
  • POSIX behavior is unaffected: $-prefixed commands don't exist in bash, Verb-Noun patterns don't match standard Unix tools, and newline splitting is correct for multi-line bash commands too
  • The snipCommand function signature and behavior for POSIX inputs is identical

@greptile-apps

greptile-apps Bot commented Jun 5, 2026

Copy link
Copy Markdown

Greptile Summary

Adds PowerShell/Windows support by introducing skip regexes for PS variable prefixes and Verb-Noun cmdlets, splitting on newlines in addition to ;/&&/||/&, and switching to OPERATOR_ONLY_RE when a heredoc is detected to avoid splitting its body.

  • POWERSHELL_SKIP_RE (^[$@&{]) and POWERSHELL_CMDLET_RE (^[A-Z][a-zA-Z]*-[A-Z]) guard PowerShell-specific constructs from being wrapped with snip; [a-zA-Z]* correctly handles mixed-case verb stems like ForEach and ConvertTo.
  • HEREDOC_RE detects <<DELIM markers and falls back to OPERATOR_ONLY_RE (no newline splitting) for the whole command, preventing the heredoc body from being fragmented.
  • Per-segment pipe handling is extracted into the new snipSegment helper so that pipe detection works correctly for each newline-separated segment independently.

Confidence Score: 5/5

Safe to merge. All three issues flagged in the previous review round are correctly fixed, and no new defects are introduced.

The POWERSHELL_CMDLET_RE regex now uses [a-zA-Z]* so mixed-case verb stems like ForEach and ConvertTo are properly caught. Tests for @Args and @() confirm the @-prefix skip path. The heredoc guard (HEREDOC_RE + OPERATOR_ONLY_RE) prevents newline-splitting inside heredoc bodies, and three dedicated tests verify all relevant cases. The snipSegment refactor correctly scopes pipe detection to individual segments after the split.

No files require special attention.

Important Files Changed

Filename Overview
src/index.ts Core logic update: adds PowerShell skip/cmdlet regexes, newline splitting via OPERATOR_RE, heredoc detection with fallback to OPERATOR_ONLY_RE, and the snipSegment helper for per-segment pipe handling. All previously raised issues are correctly addressed.
src/index.test.ts 14 new test cases covering PowerShell env vars, cmdlets (including ForEach-Object and ConvertTo-Json), call operator, splatting, newline splitting, and heredoc safety; all three previously flagged gaps now have direct coverage.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[toolExecuteBefore] --> B{starts with snip?}
    B -- yes --> Z[return unchanged]
    B -- no --> C{HEREDOC_RE detected?}
    C -- yes --> D[use OPERATOR_ONLY_RE]
    C -- no --> E[use OPERATOR_RE with newlines]
    D --> F[split on separator]
    E --> F
    F --> G{single segment?}
    G -- yes --> H[snipSegment]
    G -- no --> I[map segments]
    I --> J{is operator token?}
    J -- yes --> K[keep as-is]
    J -- no --> H
    H --> L[findFirstPipe]
    L --> M{pipe found?}
    M -- yes --> N[snipCommand firstCmd plus rest]
    M -- no --> O[snipCommand segment]
    N --> P[snipCommand]
    O --> P
    P --> Q{unproxyable cmd?}
    Q -- yes --> Z
    Q -- no --> R{POWERSHELL_SKIP_RE?}
    R -- yes --> Z
    R -- no --> S{POWERSHELL_CMDLET_RE?}
    S -- yes --> Z
    S -- no --> T[prepend snip]
Loading

Reviews (3): Last reviewed commit: "feat: add Windows/PowerShell support" | Re-trigger Greptile

Comment thread src/index.ts Outdated
Comment thread src/index.ts
Comment on lines +9 to +14
// PowerShell: segments starting with these chars are never external commands.
// $ → variable/assignment ($env:CI='true', $x = 1)
// @ → here-string, splat, array (@", @(), @{})
// & → call operator (& "path\to\exe")
// { → script block
const POWERSHELL_SKIP_RE = /^[$@&{]/

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 @-prefix path lacks test coverage

POWERSHELL_SKIP_RE includes @ to handle PowerShell here-strings (@"), splatting (@args), and array literals (@()), but there is no corresponding test. A test for e.g. @args or @("a","b") would confirm the skip path actually works and guard against future refactors.

Suggested change
// PowerShell: segments starting with these chars are never external commands.
// $ → variable/assignment ($env:CI='true', $x = 1)
// @ → here-string, splat, array (@", @(), @{})
// & → call operator (& "path\to\exe")
// { → script block
const POWERSHELL_SKIP_RE = /^[$@&{]/
// PowerShell: segments starting with these chars are never external commands.
// $ → variable/assignment ($env:CI='true', $x = 1)
// @ → here-string, splat, array (@", @(), @{})
// & → call operator (& "path\to\exe")
// { → script block
// NOTE: add a test for @-prefixed segments (splatting / here-strings) in index.test.ts
const POWERSHELL_SKIP_RE = /^[$@&{]/

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@baltika10 baltika10 force-pushed the feat/powershell-support branch from 284163d to b099f1a Compare June 5, 2026 10:14
@baltika10

Copy link
Copy Markdown
Author

Both review findings addressed in the latest force-push (b099f1a):

  1. ForEach-Object escapes cmdlet guard — fixed POWERSHELL_CMDLET_RE from /^[A-Z][a-z]+-[A-Z]/ to /^[A-Z][a-zA-Z]*-[A-Z]/ so camelCase verbs (ForEach-Object, ConvertTo-Json, etc.) are correctly skipped. Added 2 tests.
  2. @-prefix path lacks test coverage — added 2 tests: @args (splatting) and @("a","b") (array literal).

All 48 tests pass (30 original + 18 new).

Comment thread src/index.ts
- Detect and skip PowerShell env var assignments (\='Y')
- Detect and skip PowerShell Verb-Noun cmdlets (Write-Output, Get-ChildItem, etc.)
- Detect and skip PowerShell special syntax (\$, @, &, {)
- Add newline splitting to OPERATOR_RE so multi-line commands are snipped per-line
- Extract snipSegment helper for per-segment pipe handling
- Add 14 tests covering PowerShell patterns and newline splitting
@baltika10 baltika10 force-pushed the feat/powershell-support branch from b099f1a to 0177604 Compare June 5, 2026 10:34
@baltika10

Copy link
Copy Markdown
Author

Addressed the P1 heredoc finding in the latest push (0177604):

Heredoc commands silently broken by \n split — added HEREDOC_RE (/<<-?\s*['"]?\w/) detection. When the command contains a heredoc marker, newline splitting is disabled and only operator splitting (&&, ||, ;, &) is used. This preserves the heredoc body intact while still splitting on operators.

Also fixed a subtle bug where the .map() separator test used OPERATOR_RE (with newlines) regardless of which regex was used for splitting — now uses the same separator variable for both splitting and testing.

Added 3 heredoc safety tests:

  • cat <<EOF\ndata\nEOF — body preserved, command snipped
  • cat <<'EOF'\ndata\nEOF — quoted delimiter variant
  • cat <<EOF\ndata\nEOF && echo done — operators still split when heredoc present

All 51 tests pass (30 original + 21 new).

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/PowerShell: plugin breaks env guards and fails on cmdlets

1 participant