Skip to content

fix(terminal): use detected shell for execa and vscode terminal creation (#321)#333

Open
F915 wants to merge 11 commits into
Zoo-Code-Org:mainfrom
F915:main
Open

fix(terminal): use detected shell for execa and vscode terminal creation (#321)#333
F915 wants to merge 11 commits into
Zoo-Code-Org:mainfrom
F915:main

Conversation

@F915
Copy link
Copy Markdown

@F915 F915 commented May 26, 2026

Related GitHub Issue

Closes: #321

Description

Zoo Code correctly detects the user's configured default shell (e.g., PowerShell) and tells the AI to generate commands using that shell's syntax, but the Inline Terminal actually executed commands in cmd.exe. This caused:

  • PowerShell-specific commands (like Write-Host) to fail with "not recognized" errors
  • Non-ASCII output garbling on systems with non-ASCII locales — cmd.exe encodes output using the system codepage (e.g., CP936 for zh-CN) rather than UTF-8

This PR connects shell detection to both terminal execution paths so the user's configured shell is used consistently.


The work evolved in two phases:

Phase 1 (original PR) passed getShell() to both execution paths — execa({ shell: getShell() }) for inline terminal and createTerminal({ shellPath: getShell() }) for external terminal.

Phase 2 (follow-up) was prompted by review feedback from @edelauna, who pointed out that passing getShell() as shellPath on Windows with WSL configured was problematic — the function could return a Unix path (/bin/bash) that isn't valid as a Windows shellPath. Further investigation revealed that the root issue wasn't specific to WSL: the existing shell detection in shell.ts used manual VS Code config parsing with a ~95-entry allowlist, an approach that was inherently fragile and could diverge from VS Code's own internal shell resolution.

Phase 2 addressed this at the root by replacing the manual allowlist-based detection with vscode.env.shell — the API VS Code itself uses and has provided. This eliminated the mismatch entirely and allowed several downstream simplifications.


Phase 1 — Connect shell detection to execution paths

  1. ExecaTerminalProcess.ts — Changed shell: true to shell: getShell(). The detected shell is now passed to execa subprocess execution.
  2. Terminal.ts — Added shellPath: getShell() to vscode.window.createTerminal().
  3. ExecaTerminalProcess.spec.ts — Assertions updated from shell: true to shell: "/mock/fallback-shell".
  4. TerminalRegistry.spec.ts — Assertions updated to include the new shellPath parameter.

Phase 2 — Consolidate shell detection and fix WSL support

  1. shell.ts — The largest single change. Removed SHELL_ALLOWLIST (~95 entries), getWindowsShellFromVSCode(), getMacShellFromVSCode(), getLinuxShellFromVSCode(), and getShellFallbackOccurred(). getShell() now reads vscode.env.shell directly, falling back through os.userInfo(), COMSPEC/SHELL environment variables, and platform defaults. When vscode.env.shell resolves to wsl.exe, it is canonicalized to C:\Windows\System32\wsl.exe so downstream code can reliably detect WSL.

  2. Terminal.ts — Three-branch constructor replaces the single shellPath: getShell() call: when a WSL profile is detected, shellPath is omitted so VS Code's WSL profile handles PTY bridge setup; when the user has set execaShellPath, it is passed explicitly to honor that preference; otherwise, shellPath is omitted to defer to VS Code's profile system. The shellIntegrationReady promise is now started in the constructor, reducing per-command wait time.

  3. TerminalProcess.ts — Two corrections to the fallback path:

    • Removed getShellFallbackOccurred() check. Both paths now derive from the same VS Code resolution; the mismatch scenario no longer exists.
    • Removed sendText before no_shell_integration fallback. Previously, the code called terminal.sendText(command) and then emitted no_shell_integration, resulting in double execution — the command ran once silently in the external terminal, then again via execa. The fallback now emits no_shell_integration directly.
  4. ExecaTerminalProcess.ts — Uses BaseTerminal.getExecaShellPath() || getShell() for shell resolution, matching Terminal.ts. When the resolved shell is wsl.exe, spawns it directly with array arguments — wsl.exe --cd <wsl-mount-path> -- bash -c <command> — avoiding nested-quoting issues through intermediate shells. The current working directory's drive letter (e.g., C:\) is converted to the corresponding WSL mount point (/mnt/c/).

  5. Tests (7 files updated, 1 new test added) — Updated for the new shell detection chain and WSL behavior.


Known Limitations:

  1. Shell integration support: VS Code shell integration is only supported on a limited set of shells — Linux/macOS: bash, fish, pwsh, zsh; Windows: Git Bash, pwsh. For any shell not in this list (including WSL, cmd.exe, and custom shells), the external terminal cannot provide shell integration and will always fall back to the inline terminal after timeout. Users of unsupported shells are encouraged to enable "Use Inline Terminal" in settings.

  2. vscode.env.shell may not match the configured default profile for certain shells: This PR replaces the old manual allowlist-based shell detection with vscode.env.shell, which resolves via VS Code's internal terminal profile system. However, VS Code's getDefaultProfile() (terminalProfileService.ts:131) filters out auto-detected profiles with !e.isAutoDetected — only profiles that have been explicitly confirmed by the user (or are among the three factory defaults: PowerShell/PSCore, Command Prompt, Git Bash) can serve as the default. Profiles like "Windows PowerShell" (Windows 11 built-in powershell.exe), "bash (MSYS2)", Cygwin, and Cmder are auto-detected but not in the factory defaults. When one of these is selected as defaultProfile.<platform>, getDefaultProfile() returns undefined, triggering a fallback chain:

    getDefaultProfile(os) → undefined
      → getDefaultSystemShell()
        → getSystemShellWindows()
          → getFirstAvailablePowerShellInstallation()
            → PowerShell 7 (searched first)
    

    The fallback always resolves to PowerShell 7 because enumerateDefaultPowerShellInstallations() (powershell.ts:250-305) searches PSCore first; Windows PowerShell is searched last.

    This is by design, not a VS Code bug. VS Code treats auto-detected paths as untrusted (they point to user-installed third-party software that may not exist or be safe) and requires explicit user confirmation via a profiles.<platform> entry before using them as the default.

    Workaround: add an explicit entry for the desired shell in terminal.integrated.profiles.<platform> with a path:

    "terminal.integrated.profiles.windows": {
      "bash (MSYS2)": { "path": "C:\\msys64\\usr\\bin\\bash.exe" }
    }

    This clears isAutoDetected, allowing getDefaultProfile() to match.

Test Procedure

Unit tests:

cd src
pnpm test -- --run utils/__tests__/shell.spec.ts
pnpm test -- --run integrations/terminal/__tests__/

Type checking:

pnpm check-types

Build:

pnpm vsix

Manual verification (Windows with PowerShell configured):

Prerequisites: PowerShell configured with UTF-8 encoding.

  • Write-Host "test" — previously failed with 'Write-Host' is not recognized; now correctly outputs test.
  • echo "中文测试" — previously produced garbled output (CP936 encoding) on zh-CN systems; now correctly outputs 中文测试 (UTF-8).

WSL verification (Windows with WSL configured as default profile):

  • Inline terminal executes WSL commands via direct wsl.exe spawn — no nested-quoting errors.
  • External terminal waits for shell integration (timeout), then falls back to inline terminal.

Pre-Submission Checklist

  • Issue Linked: This PR is linked to an approved GitHub Issue (see "Related GitHub Issue" above).
  • Scope: My changes are focused on the linked issue (one major feature/fix per PR).
  • Self-Review: I have performed a thorough self-review of my code.
  • Testing: New and/or updated tests have been added to cover my changes (if applicable).
  • Documentation Impact: I have considered if my changes require documentation updates (see "Documentation Updates" section below).
  • Contribution Guidelines: I have read and agree to the Contributor Guidelines.

Screenshots / Videos

N/A — backend fix with no visible UI changes.

Documentation Updates

  • No documentation updates are required.
  • Yes, documentation updates are required.

The shell integration docs need the following updates:

  1. "Use Inline Terminal (recommended)" section — the current text says "bypassing shell profiles and VS Code shell integration for reliability and faster starts" but doesn't specify what shell the inline terminal actually uses. Clarify: Inline Terminal now executes commands using the shell detected from VS Code's terminal.integrated.defaultProfile.<platform> setting. Previously it always defaulted to cmd.exe on Windows (due to shell: true in execa) or /bin/sh on Unix. The updated behavior means:

    • ON: commands run in your configured shell (e.g., PowerShell, bash, zsh) via execa, no VS Code shell integration needed
    • OFF: commands run in the VS Code terminal using your shell profile, requires shell integration
  2. Decision guide: Inline Terminal ON vs OFF — add a bullet about shell selection: "ON: Uses the shell from your VS Code default profile (same as OFF). Both modes now use the same detected shell."

  3. Troubleshooting Shell Integration — add a new subsection "Shell Integration Support by Platform" listing which shells support VS Code shell integration: Linux/macOS — bash, fish, pwsh, zsh; Windows — Git Bash, pwsh. For shells outside this list (WSL, cmd.exe, MSYS2, Cygwin, Cmder), the external terminal will never receive shell integration markers and will always time out before falling back to inline terminal. Users of these shells should keep "Use Inline Terminal" ON to skip the timeout entirely.

  4. Troubleshooting Shell Integration — add a new subsection "Garbled Command Output on Windows (Non-ASCII Locales)" covering:

    • Symptom: Chinese/Japanese/Korean characters in command output appear as garbled text (mojibake)
    • Root cause: Commands on Windows were executed via cmd.exe, which encodes output using the system codepage (e.g., CP936 for zh-CN) instead of UTF-8
    • Fix: Commands now execute in your configured shell (e.g., PowerShell), which defaults to UTF-8
    • Verification: run chcp in the inline terminal — it should show code page 65001 (UTF-8). If it shows a legacy codepage (e.g., 936), configure your shell for UTF-8: for PowerShell add $OutputEncoding = [Console]::OutputEncoding = [Text.UTF8Encoding]::UTF8 to your $Profile
  5. Troubleshooting Shell Integration — add a new subsection "Default Shell Not Matching Configured Profile" covering:

    • Symptom: AI prompt shows a different shell (e.g., PowerShell 7) than the one configured in terminal.integrated.defaultProfile.windows (e.g., "Windows PowerShell" or "bash (MSYS2)")
    • Root cause: VS Code's getDefaultProfile() filters out isAutoDetected profiles; only factory defaults (PowerShell/PSCore, Command Prompt, Git Bash) and profiles with explicit path entries pass the filter (detailed explanation in Known Limitations above)
    • Fix: add an explicit path entry in terminal.integrated.profiles.<platform>, e.g.:
      "terminal.integrated.profiles.windows": {
        "bash (MSYS2)": { "path": "C:\\msys64\\usr\\bin\\bash.exe" }
      }

Additional Notes

None.

Get in Touch

Discord: Yon (yon.sinc)

Summary by CodeRabbit

  • New Features

    • Improved WSL detection and normalized WSL handling for integrated terminals.
    • Terminal creation now respects the resolved shell and defers to host WSL behavior when appropriate.
  • Bug Fixes

    • More reliable shell-integration readiness and command execution sequencing.
    • Safer handling when shell integration or terminal markers are missing to avoid double execution.
    • Prevents nested/incorrect shell invocation across platforms.
  • Tests

    • Expanded and stabilized terminal/shell tests for consistent, platform-aware behavior.

Review Change Stack

Zoo Code Contributor and others added 2 commits May 26, 2026 16:26
…ion (Zoo-Code-Org#321)

Connect the existing getShell() to both terminal creation paths to fix
the Windows shell mismatch where AI generates pwsh syntax but commands
execute in cmd.exe.

- ExecaTerminalProcess: use getShell() instead of shell: true
- Terminal: pass shellPath: getShell() to createTerminal

Fixes Zoo-Code-Org#321
…gration (Zoo-Code-Org#321)

Update two test cases that previously expected shell: true to now
expect shell: expect.any(String), reflecting the change from
shell: true to getShell() in ExecaTerminalProcess.

Refs Zoo-Code-Org#321

Co-authored-by: Zoo Code Contributor <contributor@zoocode.dev>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 26, 2026

📝 Walkthrough

Walkthrough

Shell resolution now prefers vscode.env.shell (canonicalizing WSL); ExecaTerminalProcess uses BaseTerminal.getExecaShellPath() || getShell() with a WSL direct wsl.exe path; Terminal uses a constructor-owned shellIntegrationReady promise; TerminalProcess stream handling and tests/mocks updated.

Changes

Terminal shell & integration changes

Layer / File(s) Summary
Shell utilities and tests
src/utils/shell.ts, src/utils/__tests__/shell.spec.ts
Add WSL_EXE_PATH, getWslProfile(), and rewrite getShell() to prefer vscode.env.shell, canonicalize WSL, and fallback via userInfo/env/platform; tests reworked for new priority and WSL detection.
ExecaTerminalProcess execution and tests
src/integrations/terminal/ExecaTerminalProcess.ts, src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts
Use `BaseTerminal.getExecaShellPath()
Terminal constructor & run gating
src/integrations/terminal/Terminal.ts
Create VS Code terminal with WSL-aware shell behavior and add a constructor-owned shellIntegrationReady promise (event + polling + timeout); runCommand awaits that promise before process.run.
TerminalProcess stream handling and tests
src/integrations/terminal/TerminalProcess.ts, src/integrations/terminal/__tests__/TerminalProcess.spec.ts, src/integrations/terminal/__tests__/TerminalProcess.test.ts
Emit clearer no_shell_integration warnings, avoid duplicate emission after executeCommand submission, treat missing shell-integration start markers as normal buffered output, and update tests to match changed sendText expectations and mocks.
VSCode mock and registry/exec tests
src/__mocks__/vscode.js, src/integrations/terminal/__tests__/*
Extend Vitest vscode mock with env.shell and window.onDidChangeTerminalShellIntegration; update exec and registry tests to rely on vscode.env.shell, tighten iconPath assertions, and standardize mock reset between tests.

Sequence Diagram(s)

sequenceDiagram
  participant VSCode as VSCode API
  participant Terminal as Terminal (constructor)
  participant ShellReady as shellIntegrationReady
  participant ExecaProc as ExecaTerminalProcess
  participant WSL as wsl.exe
  VSCode->>Terminal: createTerminal (may omit shellPath for WSL)
  Terminal->>ShellReady: wait for onDidChangeTerminalShellIntegration / poll
  ShellReady->>ExecaProc: run(command) after readiness
  ExecaProc->>ExecaProc: resolve effectiveShell = getExecaShellPath() || getShell()
  alt effectiveShell == WSL_EXE_PATH
    ExecaProc->>WSL: spawn wsl.exe with ["bash","-lc", command] (+ --cd converted CWD)
  else
    ExecaProc->>ExecaProc: execa(..., { shell: effectiveShell })
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • taltas
  • hannesrudolph
  • navedmerchant
  • JamesRobert20

Poem

🐰 I nibble paths and sniff each shell,

I hop through WSL, bash, and PowerShell,
getShell whispers which road to take,
no more cmd.exe to garble the wake,
a tiny hop — the terminal breathes well.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: fixing a shell mismatch by using detected shell for both execa and VSCode terminal creation.
Linked Issues check ✅ Passed All objectives from issue #321 are fully addressed: shell detection connected to execa and Terminal creation, WSL handling improved, allowlist-based detection replaced with vscode.env.shell, and tests updated.
Out of Scope Changes check ✅ Passed All changes are within scope: core files address shell detection and terminal execution; test files validate the new behavior; mock updates support the refactored detection chain.
Description check ✅ Passed The PR description is comprehensive and well-structured, covering all required template sections with detailed context on the changes made across two phases, including testing procedures, documentation updates, and known limitations.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@F915 F915 marked this pull request as ready for review May 26, 2026 10:13
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8f5fb7a1-993e-4d1d-987f-4466eb7afe09

📥 Commits

Reviewing files that changed from the base of the PR and between b761a0a and dc4a451.

📒 Files selected for processing (3)
  • src/integrations/terminal/ExecaTerminalProcess.ts
  • src/integrations/terminal/Terminal.ts
  • src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts

Comment thread src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts Outdated
Mock getShell() to verify the fallback actually uses the detected shell
instead of just checking for any string value.

Suggested-by: CodeRabbit
@edelauna edelauna closed this May 26, 2026
@edelauna edelauna reopened this May 26, 2026
F915 and others added 3 commits May 27, 2026 09:40
…ll() integration (Zoo-Code-Org#321)

PR Zoo-Code-Org#333 added shellPath and iconPath to Terminal constructor but
TerminalRegistry.spec.ts assertions were not updated, causing 4 test
failures on ubuntu-latest CI.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/integrations/terminal/__tests__/TerminalRegistry.spec.ts (1)

39-40: ⚡ Quick win

Assert getShell() invocation to prevent hardcoded-shell regressions.

You already assert the resulting shellPath, but adding expect(shellUtils.getShell).toHaveBeenCalled() makes this test fail if production code hardcodes the same string instead of calling the resolver.

Suggested test hardening
 		it("creates terminal with PAGER set appropriately for platform", () => {
 			TerminalRegistry.createTerminal("/test/path", "vscode")
+			expect(shellUtils.getShell).toHaveBeenCalledTimes(1)

 			expect(mockCreateTerminal).toHaveBeenCalledWith({
 				cwd: "/test/path",
 				name: "Roo Code",
 				iconPath: expect.objectContaining({ id: expect.any(String) }),
 				env: {
 					PAGER,
 					ROO_ACTIVE: "true",
 					VTE_VERSION: "0",
 					PROMPT_EOL_MARK: "",
 				},
 				shellPath: "/mock/fallback-shell",
 			})
 		})

Also applies to: 60-61, 83-84, 107-108, 130-131

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/integrations/terminal/__tests__/TerminalRegistry.spec.ts` around lines 39
- 40, Add assertions that the shell resolver is actually invoked: after each
vi.spyOn(shellUtils, "getShell").mockReturnValue("/mock/fallback-shell") in the
TerminalRegistry.spec tests, add expect(shellUtils.getShell).toHaveBeenCalled()
(or toHaveBeenCalledTimes(1)) to ensure production code calls
shellUtils.getShell rather than hardcoding the path; apply this to the
occurrences around the tests that set up mocked shells (the blocks using
vi.spyOn(shellUtils, "getShell") and asserting shellPath).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/integrations/terminal/__tests__/TerminalRegistry.spec.ts`:
- Around line 39-40: Add assertions that the shell resolver is actually invoked:
after each vi.spyOn(shellUtils,
"getShell").mockReturnValue("/mock/fallback-shell") in the TerminalRegistry.spec
tests, add expect(shellUtils.getShell).toHaveBeenCalled() (or
toHaveBeenCalledTimes(1)) to ensure production code calls shellUtils.getShell
rather than hardcoding the path; apply this to the occurrences around the tests
that set up mocked shells (the blocks using vi.spyOn(shellUtils, "getShell") and
asserting shellPath).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 11da761d-d24d-4448-bec2-2eb4363d25fa

📥 Commits

Reviewing files that changed from the base of the PR and between 9205427 and 3ae0a88.

📒 Files selected for processing (1)
  • src/integrations/terminal/__tests__/TerminalRegistry.spec.ts

@codecov
Copy link
Copy Markdown

codecov Bot commented May 27, 2026

Codecov Report

❌ Patch coverage is 72.22222% with 25 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/integrations/terminal/Terminal.ts 52.27% 21 Missing ⚠️
src/integrations/terminal/TerminalProcess.ts 50.00% 3 Missing ⚠️
src/integrations/terminal/ExecaTerminalProcess.ts 97.50% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

@proyectoauraorg proyectoauraorg left a comment

Choose a reason for hiding this comment

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

LGTM! Using the detected shell for both execa and VS Code terminal creation resolves the shell mismatch issue on Windows. Clean fix.

Comment thread src/integrations/terminal/Terminal.ts Outdated
const env = Terminal.getEnv()
const iconPath = new vscode.ThemeIcon("rocket")
this.terminal = terminal ?? vscode.window.createTerminal({ cwd, name: "Roo Code", iconPath, env })
this.terminal = terminal ?? vscode.window.createTerminal({ cwd, name: "Roo Code", iconPath, env, shellPath: getShell() })
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If the user has WSL configured but no explicit shell path, getShell() returns /bin/bash (a Unix path) — does passing that as shellPath actually work on Windows, or would wsl.exe be needed here?

Copy link
Copy Markdown
Author

@F915 F915 May 30, 2026

Choose a reason for hiding this comment

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

Good catch, and you're right that this was a real issue.

After further analysis, we've addressed this in the follow-up refactoring (commit 387ca89). The terminal constructor now checks for WSL configuration first — when WSL is detected, shellPath is omitted so VS Code's own WSL profile handles shell selection. Passing an explicit shell path would bypass VS Code's profile system.

With this fix, the inline terminal path can now correctly execute WSL commands. However, the external terminal path still cannot work with WSL, because VS Code shell integration does not support WSL — the supported shells on Windows are limited to Git Bash and pwsh. In practice, the external terminal will wait for shell integration until timeout, then fall back to the inline terminal — which is the expected behavior.

These changes are part of a broader consolidation of shell detection that the WSL issue prompted us to revisit. The upstream code manually parsed VS Code's terminal profile configuration with a shell allowlist, but this approach was inherently fragile — it could not reliably match VS Code's internal shell resolution, which is exactly the kind of mismatch your comment pointed out. To address this at the root, we removed the allowlist and manual parsing in favor of vscode.env.shell, the API VS Code has provided for this purpose. As a result, the two command execution paths now share the same shell resolution logic, so there is no longer a risk of falling back to a different shell than the one the external terminal used. A safety check that guarded against this mismatch — but relied on the same fragile detection it was meant to protect against — became redundant and was removed.

We also removed a sendText call that previously fired before the fallback when shell integration was unavailable — this could cause the command to execute in the external terminal without output capture, and then again via the inline path, resulting in unintended double execution. The fallback to the inline terminal now happens directly.

Thanks again for taking the time to review — the feedback was helpful in tracing this issue to its root cause.

@F915 F915 deleted the branch Zoo-Code-Org:main May 29, 2026 01:27
@F915 F915 closed this May 29, 2026
@F915 F915 deleted the main branch May 29, 2026 01:27
@F915 F915 restored the main branch May 29, 2026 01:42
@F915 F915 reopened this May 29, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (3)
src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts (1)

176-212: 💤 Low value

Unusual semicolon-prefixed statement formatting.

Lines 180, 189, 198, and 207 use a semicolon prefix before calling trimRetrievedOutput(). This is syntactically valid but unconventional and reduces readability.

♻️ Cleaner formatting
 		it("clears buffer when all output has been retrieved", () => {
 			terminalProcess["fullOutput"] = "test output data"
 			terminalProcess["lastRetrievedIndex"] = 16
-				; (terminalProcess as any).trimRetrievedOutput()
+			;(terminalProcess as any).trimRetrievedOutput()

 			expect(terminalProcess["fullOutput"]).toBe("")

Or wrap the call to avoid ASI ambiguity:

-			terminalProcess["lastRetrievedIndex"] = 16
-				; (terminalProcess as any).trimRetrievedOutput()
+			terminalProcess["lastRetrievedIndex"] = 16
+			const instance = terminalProcess as any
+			instance.trimRetrievedOutput()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts` around
lines 176 - 212, The tests use unconventional semicolon-prefixed calls like
;(terminalProcess as any).trimRetrievedOutput() which is unnecessary and harms
readability; update each test to call the method directly (e.g.,
(terminalProcess as any).trimRetrievedOutput()) or otherwise wrap the expression
to avoid the leading semicolon—modify the four occurrences that invoke
trimRetrievedOutput on the terminalProcess instance to remove the prefixed
semicolons.
src/utils/__tests__/shell.spec.ts (1)

203-263: 💤 Low value

Consider restoring the original getConfiguration mock after each getWslProfile test.

The tests directly reassign vscode.workspace.getConfiguration without restoring it, which could cause test pollution if tests run in a different order or if new tests are added to this describe block.

♻️ Suggested improvement
 	describe("getWslProfile()", () => {
+		let originalGetConfiguration: typeof vscode.workspace.getConfiguration
+
 		beforeEach(() => {
 			Object.defineProperty(process, "platform", { value: "win32" })
+			originalGetConfiguration = vscode.workspace.getConfiguration
+		})
+
+		afterEach(() => {
+			vscode.workspace.getConfiguration = originalGetConfiguration
 		})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/utils/__tests__/shell.spec.ts` around lines 203 - 263, The tests for
getWslProfile directly overwrite vscode.workspace.getConfiguration and never
restore it, risking test pollution; update the describe("getWslProfile") block
to save the original getConfiguration (e.g., const originalGetConfig =
vscode.workspace.getConfiguration) in a beforeEach and restore it in an
afterEach (or use jest.spyOn(vscode.workspace, "getConfiguration") and call
mockRestore() in afterEach) so each it(...) can safely mock getConfiguration
without leaking state across tests.
src/integrations/terminal/ExecaTerminalProcess.ts (1)

37-88: 💤 Low value

effectiveCommand is assigned but unused in the WSL branch.

The variable effectiveCommand on line 47 is only used in the non-WSL else branch (line 87), but it's assigned regardless. This is minor dead code when isWslShell is true.

♻️ Suggested cleanup
 		const resolvedShell = BaseTerminal.getExecaShellPath() || getShell()
 		const isWslShell = resolvedShell === WSL_EXE_PATH

-		let effectiveShell: string | boolean = resolvedShell
-		let effectiveCommand = command
-
 		if (isWslShell) {
 			// Spawn wsl.exe directly (not through cmd.exe) to avoid nested-quoting issues.
 			// execa(file, args, options) passes args as an array — no shell interpretation.
@@ ... @@
 		} else {
 			this.subprocess = execa({
-				shell: effectiveShell,
+				shell: resolvedShell,
 				cwd: this.terminal.getCurrentWorkingDirectory(),
 				all: true,
 				stdin: "ignore",
 				env: {
 					...process.env,
 					LANG: "en_US.UTF-8",
 					LC_ALL: "en_US.UTF-8",
 				},
-			})`${effectiveCommand}`
+			})`${command}`
 		}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/integrations/terminal/ExecaTerminalProcess.ts` around lines 37 - 88, The
run method assigns effectiveCommand but never uses it in the WSL branch; remove
the dead assignment by either moving the line that sets effectiveCommand into
the non-WSL else branch or stop using effectiveCommand entirely and pass command
directly to the non-WSL execa template literal—update the run function
(referencing run, effectiveCommand, isWslShell, and the execa calls) so
effectiveCommand is only created/used where needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/integrations/terminal/__tests__/TerminalProcessExec.cmd.spec.ts`:
- Around line 52-54: The env object literal sets shell to
"C:\Windows\System32\cmd.exe" with unescaped backslashes; update the shell
string in the test (the env object, shell property) to use escaped backslashes
("C:\\Windows\\System32\\cmd.exe") so the path is a valid JavaScript string
literal and avoids accidental escape sequences.

In `@src/integrations/terminal/__tests__/TerminalProcessExec.pwsh.spec.ts`:
- Around line 52-54: The Windows path string assigned to env.shell currently
uses single backslashes which are interpreted as escape sequences; update the
test to use escaped backslashes (e.g., double backslashes) in the "shell" value
so the path reads correctly (replace "C:\Program Files\PowerShell�\pwsh.exe"
with an escaped form); locate the env object in the
TerminalProcessExec.pwsh.spec.ts test and change the shell string to use "\\"
for each backslash.

---

Nitpick comments:
In `@src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts`:
- Around line 176-212: The tests use unconventional semicolon-prefixed calls
like ;(terminalProcess as any).trimRetrievedOutput() which is unnecessary and
harms readability; update each test to call the method directly (e.g.,
(terminalProcess as any).trimRetrievedOutput()) or otherwise wrap the expression
to avoid the leading semicolon—modify the four occurrences that invoke
trimRetrievedOutput on the terminalProcess instance to remove the prefixed
semicolons.

In `@src/integrations/terminal/ExecaTerminalProcess.ts`:
- Around line 37-88: The run method assigns effectiveCommand but never uses it
in the WSL branch; remove the dead assignment by either moving the line that
sets effectiveCommand into the non-WSL else branch or stop using
effectiveCommand entirely and pass command directly to the non-WSL execa
template literal—update the run function (referencing run, effectiveCommand,
isWslShell, and the execa calls) so effectiveCommand is only created/used where
needed.

In `@src/utils/__tests__/shell.spec.ts`:
- Around line 203-263: The tests for getWslProfile directly overwrite
vscode.workspace.getConfiguration and never restore it, risking test pollution;
update the describe("getWslProfile") block to save the original getConfiguration
(e.g., const originalGetConfig = vscode.workspace.getConfiguration) in a
beforeEach and restore it in an afterEach (or use jest.spyOn(vscode.workspace,
"getConfiguration") and call mockRestore() in afterEach) so each it(...) can
safely mock getConfiguration without leaking state across tests.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: dff403bf-4ce8-4795-9eca-970a7a523af7

📥 Commits

Reviewing files that changed from the base of the PR and between 3ae0a88 and b255557.

📒 Files selected for processing (13)
  • src/__mocks__/vscode.js
  • src/integrations/terminal/ExecaTerminalProcess.ts
  • src/integrations/terminal/Terminal.ts
  • src/integrations/terminal/TerminalProcess.ts
  • src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts
  • src/integrations/terminal/__tests__/TerminalProcess.spec.ts
  • src/integrations/terminal/__tests__/TerminalProcess.test.ts
  • src/integrations/terminal/__tests__/TerminalProcessExec.bash.spec.ts
  • src/integrations/terminal/__tests__/TerminalProcessExec.cmd.spec.ts
  • src/integrations/terminal/__tests__/TerminalProcessExec.pwsh.spec.ts
  • src/integrations/terminal/__tests__/TerminalRegistry.spec.ts
  • src/utils/__tests__/shell.spec.ts
  • src/utils/shell.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/integrations/terminal/tests/TerminalRegistry.spec.ts

Comment thread src/integrations/terminal/__tests__/TerminalProcessExec.cmd.spec.ts
…-Org#321)

Replace manual VS Code config parsing and allowlist validation in shell.ts
with vscode.env.shell, the API VS Code provides for extension shell
detection since version 1.37.

- shell.ts: remove SHELL_ALLOWLIST (~95 entries) and manual config parsing
  functions (getWindowsShellFromVSCode, getMacShellFromVSCode,
  getLinuxShellFromVSCode). getShell() reads vscode.env.shell directly,
  falling back through os.userInfo(), COMSPEC/SHELL environment variables,
  and platform defaults. Remove getShellFallbackOccurred().
- Terminal.ts: construct terminal without explicit shellPath when the user
  has not set execaShellPath or configured WSL, letting VS Code's profile
  system determine the shell. When execaShellPath is set, pass it explicitly.
  For WSL, omit shellPath so VS Code uses its WSL profile for shell
  integration. Start the shell-integration-ready promise in the constructor.
- TerminalProcess.ts: remove the getShellFallbackOccurred check that
  previously guarded against mismatch between the terminal's shell and the
  detected shell. After the shell.ts refactoring, both paths use the same
  VS Code resolution. When shell integration is unavailable, emit
  no_shell_integration without calling sendText first.
  In the stream processing path, treat missing OSC 633/133 markers as
  complete output instead of an error.
- ExecaTerminalProcess.ts: use BaseTerminal.getExecaShellPath() || getShell()
  to match Terminal's shell resolution. When the shell is wsl.exe, spawn it
  directly with array arguments instead of wrapping through a shell.
- tests: update shell.spec.ts for the new getShell() fallback chain. Update
  terminal test files to mock vscode.env.shell instead of stubbing getShell().
  Extend vscode.js mock with onDidChangeTerminalShellIntegration event and
  env.shell. Update TerminalProcess.spec.ts to verify sendText is not called
  before no_shell_integration (preventing double execution). Update
  TerminalRegistry.spec.ts to drop the getShell spy and shellPath assertions
  now that Terminal no longer passes an explicit shellPath by default. Add
  WSL direct-spawn test in ExecaTerminalProcess.spec.ts.

Fixes Zoo-Code-Org#321
@F915
Copy link
Copy Markdown
Author

F915 commented May 30, 2026

@coderabbitai resume

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 30, 2026

✅ Actions performed

Reviews resumed.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/utils/__tests__/shell.spec.ts`:
- Line 3: The test currently imports existsSync as a named import so
vi.mocked(existsSync) does not actually stub the fs call used by getShell;
change the import to import * as fs from "fs" (so the module object is
available) and replace vi.mocked(existsSync).mockReturnValue(false) with
vi.spyOn(fs, "existsSync").mockReturnValue(false) (or alternatively call
vi.mock("fs") before imports and mock the module), ensuring the stubbed
existsSync is used by getShell in the test.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e0ae9fbc-7102-48cc-a5e5-957d641e1999

📥 Commits

Reviewing files that changed from the base of the PR and between 387ca89 and 55f901c.

📒 Files selected for processing (2)
  • src/utils/__tests__/shell.spec.ts
  • src/utils/shell.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/utils/shell.ts

Comment thread src/utils/__tests__/shell.spec.ts Outdated
…oo Code (Zoo-Code-Org#321)

- shell.ts: remove existsSync import and revert getWslProfile() to return
  null when no default profile is configured. Upstream PR Zoo-Code-Org#239's PowerShell
  fallback was intended for the now-deleted getWindowsShellFromVSCode() and
  was incorrectly auto-merged into getWslProfile(), causing TS2339 errors.
- shell.spec.ts: remove existsSync import and vi.mocked(existsSync) mock
  that were also carried in by the upstream merge.
- Terminal.ts, TerminalProcess.ts, TerminalRegistry.spec.ts: rename
  "Roo Code" terminal name and "Roo/PS Workaround" string to Zoo Code.
@F915 F915 requested a review from edelauna May 31, 2026 07:51
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.

[BUG] Windows: shell mismatch between AI prompt generation and Inline Terminal execution

3 participants