Skip to content

fix: add windowsHide and explicit stdio pipes to prevent pwsh.exe stdout loss#389

Open
ludoplex wants to merge 1 commit intowonderwhy-er:mainfrom
ludoplex:fix/378-pwsh-stdout-capture
Open

fix: add windowsHide and explicit stdio pipes to prevent pwsh.exe stdout loss#389
ludoplex wants to merge 1 commit intowonderwhy-er:mainfrom
ludoplex:fix/378-pwsh-stdout-capture

Conversation

@ludoplex
Copy link

@ludoplex ludoplex commented Mar 23, 2026

User description

Summary

  • Add windowsHide: true to spawn() options to prevent .NET 8 runtime (used by pwsh.exe) from allocating a new console via AllocConsole()
  • Explicitly set stdio: ['pipe', 'pipe', 'pipe'] to ensure child process output routes through Node.js pipes regardless of shell runtime

Root cause

When defaultShell is pwsh.exe (PowerShell 7), the .NET 8 runtime allocates its own console instead of inheriting Node.js stdio pipes. This causes external executables (Python, Git, Node.js) to emit stdout to a visible desktop window rather than back through the captured pipe, resulting in zero output lines for start_process and read_process_output.

Test plan

  • Verify start_process with pwsh.exe as defaultShell captures stdout from python -c "print('hello')"
  • Verify start_process with pwsh.exe captures stdout from git --version
  • Verify start_process with powershell.exe still works as before (regression check)
  • Verify start_process with cmd.exe still works as before (regression check)
  • Verify interactive REPL sessions still function correctly

Fixes #378

Generated with Claude Code


CodeAnt-AI Description

Keep command output visible when using PowerShell on Windows

What Changed

  • Command output is now routed through the app’s normal pipes on Windows, so text from tools like Python, Git, and Node shows up in the terminal again.
  • PowerShell sessions no longer open a separate console window that can steal output away from the app.
  • This applies when running commands through the default shell and when using a named shell.

Impact

✅ Fewer missing command outputs on Windows
✅ Clearer terminal logs from external tools
✅ No extra console window for PowerShell commands

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

Summary by CodeRabbit

  • Bug Fixes
    • Improved terminal command execution with standardized I/O stream handling across different shell configurations.
    • Enhanced console allocation behavior on Windows for consistent terminal output display.

…out loss

When defaultShell is pwsh.exe (PowerShell 7), child processes spawned by
start_process fail to capture stdout because .NET 8 runtime allocates a new
console via AllocConsole() instead of inheriting Node.js stdio pipes.

Add windowsHide: true to suppress new console allocation and explicitly set
stdio to [pipe, pipe, pipe] so child output routes back through Node.js
regardless of the shell runtime.

Fixes wonderwhy-er#378

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 23, 2026 10:11
@codeant-ai
Copy link
Contributor

codeant-ai bot commented Mar 23, 2026

CodeAnt AI is reviewing your PR.


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 23, 2026

📝 Walkthrough

Walkthrough

The change modifies TerminalManager.executeCommand() to explicitly configure child-process I/O with stdio: ['pipe', 'pipe', 'pipe'] and windowsHide: true for both shell-path and shell-option code paths. This addresses stdout capture failures with pwsh.exe on Windows by ensuring consistent stdin/stdout/stderr piping and preventing console window allocation.

Changes

Cohort / File(s) Summary
Child Process I/O Configuration
src/terminal-manager.ts
Explicit spawn options now set stdio: ['pipe', 'pipe', 'pipe'] and windowsHide: true to fix stdout capture from external executables when using pwsh.exe, replacing reliance on Node.js defaults.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • Fixes for terminal commands #271: Modifies spawn options in src/terminal-manager.ts with shell-specific argument configuration; directly related to the same file and child process spawning behavior.

Suggested labels

size:L

Poem

🐰 A rabbit hops through pipes so clear,
No hidden consoles to fear!
pwsh and python, now they align,
stdout flows back—a fix divine!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding windowsHide and explicit stdio pipes to fix pwsh.exe stdout loss, directly addressing the issue.
Linked Issues check ✅ Passed The code changes implement the suggested fix from issue #378 by adding windowsHide: true and stdio: ['pipe', 'pipe', 'pipe'] to prevent console allocation and ensure stdout capture.
Out of Scope Changes check ✅ Passed All changes in terminal-manager.ts are scoped to the executeCommand() method and directly address the pwsh.exe stdout loss issue without introducing unrelated modifications.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ 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.

@codeant-ai codeant-ai bot added the size:XS This PR changes 0-9 lines, ignoring generated files label Mar 23, 2026
@codeant-ai
Copy link
Contributor

codeant-ai bot commented Mar 23, 2026

Sequence Diagram

This PR updates process spawning so shell sessions always use explicit stdio pipes and hidden windows on Windows. The main effect is that commands run through pwsh now return stdout to the manager instead of losing output to a separate console window.

sequenceDiagram
    participant Client
    participant TerminalManager
    participant ShellProcess
    participant ExternalProgram

    Client->>TerminalManager: start_process with command
    TerminalManager->>ShellProcess: Spawn with piped stdio and windows hidden
    ShellProcess->>ExternalProgram: Execute requested command
    ExternalProgram-->>ShellProcess: Emit stdout
    ShellProcess-->>TerminalManager: Forward stdout through pipe
    Client->>TerminalManager: read_process_output
    TerminalManager-->>Client: Return captured output lines
Loading

Generated by CodeAnt AI

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the process-spawning logic in TerminalManager to improve stdout/stderr capture reliability on Windows when the configured default shell is PowerShell 7 (pwsh.exe), addressing the reported “external executable produces no captured output” behavior.

Changes:

  • Adds windowsHide: true to the spawn() options to avoid showing/creating a visible console window when launching shells on Windows.
  • Explicitly sets stdio: ['pipe', 'pipe', 'pipe'] so child processes consistently use Node-managed pipes for stdout/stderr capture.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

spawnConfig = getShellSpawnArgs(shellToUse, enhancedCommand);
spawnOptions = {
stdio: ['pipe', 'pipe', 'pipe'] as const,
windowsHide: true, // Prevent .NET-based shells (pwsh.exe) from allocating a new console
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

The inline comment about windowsHide: true is likely overstating what Node guarantees. In Node.js, windowsHide hides the console window (and may set CREATE_NO_WINDOW), but it doesn’t strictly “prevent” a .NET runtime from calling AllocConsole(). Consider rephrasing to reflect the actual behavior (hide the window / avoid showing a new console) so future debugging isn’t misled.

Suggested change
windowsHide: true, // Prevent .NET-based shells (pwsh.exe) from allocating a new console
windowsHide: true, // Request that Windows hide the console window (avoid showing a new console for .NET-based shells like pwsh.exe)

Copilot uses AI. Check for mistakes.
Comment on lines 159 to 162
spawnOptions = {
stdio: ['pipe', 'pipe', 'pipe'] as const,
windowsHide: true, // Prevent .NET-based shells (pwsh.exe) from allocating a new console
env: {
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

This change is addressing a Windows+pwsh-specific stdout capture regression, but there’s no automated test exercising “pwsh + external executable stdout” (e.g., pwsh -Command "python -c ..." or git --version"). Adding a Windows-gated regression test (skipping if pwsh isn’t present) would help prevent reintroducing the issue.

Copilot uses AI. Check for mistakes.
@codeant-ai
Copy link
Contributor

codeant-ai bot commented Mar 23, 2026

CodeAnt AI finished reviewing your PR.

Copy link
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/terminal-manager.ts (1)

160-161: Extract the shared spawn defaults and update the stale note.

stdio, windowsHide, and env are now part of the Windows capture fix, but they're duplicated across both branches and the note at Lines 142-143 still says no special stdio handling is needed. Pulling the shared options into one base object and updating that note will make this less likely to drift later.

♻️ Suggested cleanup
-    // Note: No special stdio options needed here, Node.js handles pipes by default
+    // Explicit pipes and `windowsHide` are part of the Windows `pwsh.exe` capture fix.
+
+    const baseSpawnOptions = {
+      stdio: ['pipe', 'pipe', 'pipe'] as const,
+      windowsHide: true,
+      env: {
+        ...process.env,
+        TERM: 'xterm-256color'
+      }
+    };
 
     if (typeof shellToUse === 'string') {
       // Use shell-specific configuration with login flags where appropriate
       spawnConfig = getShellSpawnArgs(shellToUse, enhancedCommand);
-      spawnOptions = {
-        stdio: ['pipe', 'pipe', 'pipe'] as const,
-        windowsHide: true,  // Prevent .NET-based shells (pwsh.exe) from allocating a new console
-        env: {
-          ...process.env,
-          TERM: 'xterm-256color'  // Better terminal compatibility
-        }
-      };
+      spawnOptions = { ...baseSpawnOptions };
 
       // Add shell option if needed (for unknown shells)
       if (spawnConfig.useShellOption) {
         spawnOptions.shell = spawnConfig.useShellOption;
       }
@@
-      spawnOptions = {
-        shell: shellToUse,
-        stdio: ['pipe', 'pipe', 'pipe'] as const,
-        windowsHide: true,
-        env: {
-          ...process.env,
-          TERM: 'xterm-256color'
-        }
-      };
+      spawnOptions = { ...baseSpawnOptions, shell: shellToUse };
     }

Also applies to: 181-182

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/terminal-manager.ts` around lines 160 - 161, Extract the duplicated spawn
settings (stdio, windowsHide, env) into a single shared constant (e.g.,
DEFAULT_SPAWN_OPTIONS or baseSpawnOptions) and use that object in both places
where child processes are spawned instead of repeating the array/object inline;
replace the inline { stdio: ['pipe','pipe','pipe'] as const, windowsHide: true,
env: ... } occurrences with a spread/merge off the shared constant so per-call
overrides remain possible, and update the nearby stale comment about stdio
handling to reflect that special stdio/windowsHide/env options are now
intentionally applied for the Windows capture fix.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/terminal-manager.ts`:
- Around line 160-161: Extract the duplicated spawn settings (stdio,
windowsHide, env) into a single shared constant (e.g., DEFAULT_SPAWN_OPTIONS or
baseSpawnOptions) and use that object in both places where child processes are
spawned instead of repeating the array/object inline; replace the inline {
stdio: ['pipe','pipe','pipe'] as const, windowsHide: true, env: ... }
occurrences with a spread/merge off the shared constant so per-call overrides
remain possible, and update the nearby stale comment about stdio handling to
reflect that special stdio/windowsHide/env options are now intentionally applied
for the Windows capture fix.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 09e5c506-e1f4-424d-af12-ceb717410397

📥 Commits

Reviewing files that changed from the base of the PR and between d854870 and 6a4a501.

📒 Files selected for processing (1)
  • src/terminal-manager.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XS This PR changes 0-9 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

start_process captures no stdout from external executables when defaultShell is pwsh.exe

2 participants