Windows: Console window flashing when executing tools

Status Open
Reported on v2.0.74
Maintainer reply None cached
Activity 63 comments · opened Dec 20, 2025

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

## Description
When using Claude Code on Windows (Windows Terminal), a console window briefly flashes/appears every time a tool is executed (Bash, Grep, Glob, Read, etc.).

## Environment

  • OS: Windows
  • Terminal: Windows Terminal
  • Platform: win32

## Steps to Reproduce

  1. Run Claude Code on Windows
  2. Execute any command or let Claude use any tool
  3. Observe brief window flash

## Expected Behavior
Tools should execute without visible window flashing.

## Possible Solution
When creating child processes on Windows, use CREATE_NO_WINDOW flag or STARTUPINFO.dwFlags = STARTF_USESHOWWINDOW with wShowWindow = SW_HIDE.

## Workaround
Currently using WSL as a workaround.

---

What Should Happen?

11

Error Messages/Logs

Steps to Reproduce

11

Claude Model

None

Is this a regression?

Yes, this worked in a previous version

Last Working Version

_No response_

Claude Code Version

2.0.74

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Terminal.app (macOS)

Additional Information

_No response_

View original on GitHub ↗

63 Comments

GigiTiti-Kai · 8 months ago

I'm also experiencing this issue, and it extends beyond just tool execution.

Additional Context

The console window flashing also occurs when plugin hooks execute. For example, the claude-mem plugin registers hooks for SessionStart, UserPromptSubmit, PostToolUse, and Stop events that run node and bun commands. Each execution creates a visible console window.

What I tried (unsuccessfully)

  1. VBS wrapper with hidden window - Wrapped commands in a VBS script using WScript.Shell.Run with vbHide flag. Failed due to quote escaping issues with cmd /c.
  1. PowerShell -WindowStyle Hidden - Wrapped commands with powershell -WindowStyle Hidden -Command "...". The PowerShell window was hidden, but the spawned node.exe/bun.exe processes still created their own console windows.

Root Cause

On Windows, console applications (like node.exe, bun.exe) create console windows by default when spawned. The only reliable way to prevent this is to use the CREATE_NO_WINDOW flag when calling CreateProcess().

Environment

  • OS: Windows 11
  • Terminal: Windows Terminal
  • Claude Code with claude-mem plugin

Request

Please consider implementing CREATE_NO_WINDOW for all child process spawning on Windows, including:

  • Tool execution (Bash, Grep, etc.)
  • Plugin hook execution
  • MCP server processes

This would significantly improve the Windows user experience.

TheDan64 · 7 months ago

I've recently started to experience this on windows and it's a really bad user experience. You can't do anything else while claude is running or it'll get in the way

EngDawood · 7 months ago

yes , it's very annoying

PakAbhishek · 7 months ago

Hook execution also affected - critical UX impact for plugin users

User Experience Impact:

I'm experiencing this same issue with plugin hooks, not just tool execution. Every time a claude-mem plugin hook executes (SessionStart, UserPromptSubmit, PostToolUse, Stop), a Node.js console window flashes on screen, often stealing keyboard focus mid-sentence. This happens dozens of times per session and significantly disrupts workflow.

Root Cause:

The issue is in Claude Code's hook execution function (appears to be GE1() in the bundled CLI). The spawn call is missing the windowsHide option:

Current Code:

let V = spawn(command, [], {env: K, cwd: a1(), shell: true})

Required Fix:

let V = spawn(command, [], {
  env: K,
  cwd: a1(),
  shell: true,
  windowsHide: process.platform === 'win32'
})

Precedent:

This exact fix was already implemented in:

  • claude-agent-sdk-typescript (Issue anthropics/anthropic-sdk-typescript#103)
  • Standard Node.js practice for Windows subprocess spawning

Why Workarounds Don't Work:

All attempted workarounds fail because:

  • PowerShell -WindowStyle Hiddencmd.exe spawns first before PowerShell
  • VBScript wrappers → Can't handle stdin properly (hooks need JSON input via stdin)
  • Batch files with start /b → Still creates initial cmd.exe window
  • PATH shims → Would also flash windows

The ONLY solution is fixing the spawn call at the source.

Impact Scope:

This affects:

  • ✅ All Windows users of Claude Code
  • ✅ Every tool execution (Bash, Read, Write, etc.)
  • ✅ Every plugin hook execution (SessionStart, UserPromptSubmit, PostToolUse, Stop)
  • ✅ MCP server startups via hooks

Business Impact:

For professional Windows users, this makes Claude Code nearly unusable. The constant window flashing:

  • Disrupts typing mid-sentence
  • Breaks focus during code reviews
  • Makes screen recording unprofessional
  • Prevents use in client-facing environments

Request:

Could this be prioritized for the next patch release? The fix is a one-line change that would immediately improve UX for all Windows users. I'm happy to test a beta build if that would help.

Environment:

  • OS: Windows 11 64-bit
  • Claude Code version: v2.1.2
  • Plugin: claude-mem v9.0.0
  • Terminal: PowerShell 7.x

Thank you for considering this!

roy-enginner · 7 months ago

Hi team — adding an update since my duplicate report (#16880) was closed.

I can still reproduce the same “console window flash” on Windows when tools run, now on the VS Code extension as well:

  • Claude Code for VS Code extension: 2.1.11
  • OS: Windows 11
  • Where it happens: VS Code (integrated terminal / Claude panel) — every Bash/tool invocation triggers a brief node.exe/console window flash

The worst part is that the flashing window steals foreground focus while it appears. During that moment I can’t interact with VS Code (keyboard input gets interrupted / clicks can be lost), so it effectively blocks other work. This is not just distracting — it actively breaks workflow.

Could you share the current status and when you expect a fix (e.g., a target version or timeframe)?
Thanks.

jphoenix-dev · 7 months ago

This is extraordinarily disruptive when using CC on Win11. One workaround is to use an admin terminal but doing so creates its own downstream issues and shouldn't be necessary. This is seriously impacting workflow - desperate for a fix.

Update: Another workaround for those finding this really annoying; commands run by agents don't appear to trigger the flashing cmd window issue (at least for me on win11, Terminal, CC CLI, non-admin account), so if you can delegate a task via agents I recommend doing so.

DamianReeves · 7 months ago

FYI I was plagued by this issue (it was immensely annoying) and finally today I got it fixed, this did the trick (note I have my git installed via scoop):

{
  "autoUpdatesChannel": "stable",
  "env": {
    "SHELL": "C:\\Users\\redacted\\scoop\\apps\\git\\2.52.0\\bin\\bash.exe"
  }
}
Jetski5822 · 7 months ago

Omg, please fix this - its pretty much impossible to use Claude Code in a windows env with this popup.

I may need to try @DamianReeves workaround.

blueberrynotstraw · 7 months ago

I also suffer from this issue, and it makes Claude Code nearly unusable. I may give up usage of it entirely, or always run it on a 2nd desktop.

I experience this through CMD line Claude, as well Claude through VSCode

I had no luck attempting @DamianReeves 's workaround

Clinteastman · 6 months ago

This is so annoying. Constantly having terminal windows flashing up when I'm working is a pain. Can we please have a fix for this?

jarrieta86 · 6 months ago

Same issue here. Using Claude Desktop (not CLI) on Windows 11 with working directory on WSL (\\wsl.localhost\Ubuntu\home\...).

Every message triggers a visible /usr/bin/bash --login -i -c cygpath -u \\wsl.localhost\... window for git repo detection. The window flashes briefly and steals focus, making it very disruptive.

Attempted workarounds (none worked):

Setting SHELL to cmd.exe in ~/.claude/settings.json
Setting SHELL to powershell.exe
Removing SHELL env entirely
The windowsHide: true fix on child_process.spawn() would resolve this.

Environment:

Windows 11
Claude Desktop (latest)
Git for Windows installed (C:\Program Files\Git\cmd\git.exe)
Working directory: WSL Ubuntu via \\wsl.localhost\

rsparacia · 6 months ago

Also experiencing the same issue here, rendering Claude Code incredibly tedious to use. Using Claude Code CLI (native installer) on Windows 11

Every Bash tool execution spawns a visible Git Bash window (/usr/bin/bash --login -i -c) that flashes open and closed.
The window steals focus mid-typing, making the experience very disruptive.

Attempted workarounds (none worked):

  • Setting CLAUDE_CODE_SHELL to powershell in ~/.claude/settings.json
  • Setting CLAUDE_CODE_SHELL to pwsh (PowerShell Core, freshly installed)
  • Setting Windows Terminal as default terminal app
  • Launching Claude Code from pwsh instead of default terminal

The windowsHide: true fix on child_process.spawn() would resolve this.

Environment:

  • Windows 11 Enterprise 10.0.26100
  • Claude Code native installer (~/.local/bin/claude.exe)
  • Git for Windows installed (AppData\Local\Programs\Git)
  • PowerShell Core installed
  • Windows Terminal as default terminal
ilude · 6 months ago

Regression: not present in 2.1.42, confirmed in 2.1.47

This issue appears to be a regression introduced between v2.1.42 and v2.1.47. On v2.1.42, no CMD window flashing or focus stealing occurs during tool execution. On v2.1.47, every tool call spawns a visible console window that steals keyboard focus.

Timing aligns with the new "session environment" / shell snapshotting mechanism introduced in v2.1.45, which also caused other Windows regressions (#26481, #26610, #26746). The new shell mechanism likely changed how child processes are spawned, dropping the windowsHide flag or spawning via a different code path that doesn't suppress console windows.

Environment:

  • OS: Windows 11 (MSYS2 / Git Bash)
  • Terminal: Windows Terminal
  • Working version: v2.1.42
  • Broken version: v2.1.47
jphoenix-dev · 6 months ago

This issue appears to have been fixed, at least for me (Win 11, CC CLI, v2.1.50 but v2.1.47 also worked) - CC is saying it can see windowsHide: true" in the bash shell spawn and I'm not getting flashing cmd windows on tool usage on my non-admin user account. Can't see anything in the changelog, but great its addressed.

Edit: worth noting I'm still on the original npm version of CC CLI, not the new "native" install approach, in case that makes a difference.

ilude · 6 months ago

Linking this here: https://github.com/anthropics/claude-code/issues/28138#issuecomment-3959696822

Is this going to get addressed? I am stuck on 2.1.42 which seems to be the last working version before this bug was introduced!

I dug into it and here is what I can tell based on the available info to the public:

Windows: Console window flashing regression introduced in v2.1.45

Since v2.1.45, every tool call, hook execution, and MCP server startup on Windows spawns a visible cmd.exe/conhost.exe window that flashes on screen and steals keyboard focus. v2.1.42 is the last version without this behavior.

This has been reported independently by many users across 14+ issues over the past two months with no official response. Posting this as a consolidated comment with root cause analysis to hopefully get some traction.

What happens

When Claude Code executes any tool (Bash, Grep, Glob, Read, etc.), runs a hook, or starts an MCP server, a black console window briefly appears and steals keyboard focus. If you're typing in another application, keystrokes get swallowed. If you have hooks running on every prompt or tool call, the flashing is nearly continuous. It makes Claude Code effectively unusable on Windows without pinning to v2.1.42.

Root cause analysis

The regression aligns precisely with the introduction of the session environment / shell snapshotting mechanism in v2.1.45. Here's the evidence:

  1. v2.1.27 originally fixed console window flashing by adding windowsHide: true to child_process.spawn() calls
  2. v2.1.45 introduced shell snapshotting — a mechanism that captures and replays the user's shell environment. This created new spawn call sites that don't carry the windowsHide: true fix from v2.1.27
  3. The error message "Session environment not yet supported on Windows" (#26610) confirms the feature shipped without full Windows support
  4. The onecmd leak in $SHELLOPTS (#26481) shows the snapshotting mechanism uses bash -o onecmd to capture environment, and this flag leaks into user sessions
  5. Technical investigation in #27115 confirmed that windowsHide: true IS set in 12 places in the binary, but the ConPTY/SEA binary context causes conhost.exe allocation anyway — the new spawn paths introduced by shell snapshotting likely bypass or override these settings

Additional changes in v2.1.45 that may contribute:

  • "Propagating API provider environment variables to tmux-spawned processes" — if env construction was refactored, spawn options may have been lost
  • "Improved memory usage for shell commands that produce large output" — likely touched spawn call sites

v2.1.47 made things worse with "Fixed hooks silently failing on Windows by using Git Bash instead of cmd.exe" (#25981), which created yet another spawn path. That same version also introduced hook path mangling on Windows (#26746) where backslash separators get stripped.

Related issues

Primary reports:

  • #14828 — Console window flashing when executing tools (9 👍, 13 comments, open since Dec 2025)
  • #27115 — Plugin hooks spawn visible cmd.exe/conhost.exe despite windowsHide:true
  • #28138 — Bash commands spawn visible black console windows
  • #20814 — Constant console window popups and zombie node.exe processes
  • #19391 — Windows popup issue

Duplicate/related reports:

  • #19012 — Hook commands cause brief console window flash (contains COMSPEC wrapper workaround)
  • #17230 — Feature request: add windowsHide option for hooks
  • #16880 — Console window flashes on every Bash tool execution (closed as dup)
  • #15572 — Console windows flash, missing windowsHide option (closed as dup)
  • #24708 — MCP stdio servers flash visible CMD windows (closed as dup)
  • #26440 — Bash popup at startup due to -i flag in cygpath call (closed as dup)
  • #21375 — Bun console window appears when starting Claude Code
  • #23229 — Persistent bun.exe terminal window with EPERM error

Same-root-cause regressions from v2.1.45:

  • #26481 — Bash tool returns exit code 1 (onecmd in SHELLOPTS)
  • #26610 — "Session environment not yet supported on Windows"
  • #26746 — Hook paths broken (backslash stripping)

Workarounds

| Workaround | Effectiveness |
|------------|--------------|
| Pin to v2.1.42 + DISABLE_AUTOUPDATER=1 | Fully works, but stuck on old version |
| Custom COMSPEC wrapper (details in #19012) | Fully works, requires compiling a C++ GUI-subsystem exe |
| Set CLAUDE_CODE_GIT_BASH_PATH to C:\Program Files\Git\bin\bash.exe | Works for some users |
| Use WSL instead of native Windows | Works but changes the whole workflow |
| Disable plugin hooks (claude-mem, etc.) | Reduces frequency but doesn't eliminate |

What a fix probably looks like

Based on community investigation, the fix needs to ensure windowsHide: true (or the Win32 CREATE_NO_WINDOW flag) is set on all child_process.spawn() and child_process.exec() calls — including the ones introduced by the shell snapshotting mechanism in v2.1.45 and the hook execution path changed in v2.1.47. The MCP SDK also has a bug where windowsHide is conditional on "type" in process which only returns true in Electron, so it's effectively always false in Node.js (#24708).

Environment

  • Windows 11 (also reported on Windows 10)
  • Claude Code v2.1.45+ (last working: v2.1.42)
  • Affects: CLI (VS Code extension and Claude Desktop also reported in some issues but unverified)
  • Terminals tested: Windows Terminal, VS Code integrated terminal, cmd.exe, PowerShell
ilude · 6 months ago

<img width="2286" height="1042" alt="cmd-exe-hydra.png — cut off one window, two more shall take its place" src="https://github.com/user-attachments/assets/fa5e4e9b-8c2b-4c9b-b5c8-5e0cfc5fd4aa" />

Tell me you don't use windows to develop claude code without saying you don't use windows!

Just try opening the start menu or typing while this is going on....

ilude · 6 months ago

Update: Narrowed down the hook-specific flashing on v2.1.59 (npm). Internal tool calls do not flash with hooks disabled — the regression appears to be specifically on the hook execution spawn path where windowsHide/CREATE_NO_WINDOW was lost in v2.1.45+.

Any hook command that spawns a separate Windows console-subsystem binary (like uv.exe or uvw.exe) triggers a visible conhost.exe window. Commands that run inside the existing bash process (echo, python) do not flash. This is consistent with v2.1.42 working — the hooks called uv run on both versions, but v2.1.42 suppressed the console allocation.

Workaround: use bare python instead of uv run in hook commands.

Full testing details: #28138 comment.

cruzlauroiii · 5 months ago

A fix is available as a Claude Code plugin: powershell-default

Install:

/plugin marketplace add cruzlauroiii/claude-code
/plugin install powershell-default@cruzlauroiii-plugins

Adds a native Pwsh tool (shows as Pwsh(...) in the UI). Commands use PowerShell syntax directly. When enabled, Bash tool is blocked. Works on any OS with PowerShell 7+.

PR: https://github.com/anthropics/claude-code/pull/35761

kamiletar · 5 months ago

Additional data: Procmon analysis reveals burst spawns on session start

Environment: Windows 11 Enterprise 10.0.26200, Claude Code 2.1.78, Claude Desktop

Finding: The problem is not just the 10s polling — it's burst spawns

Using Process Monitor (Sysinternals), I captured all Process Create events from Claude.exe. Two distinct patterns:

1. Background polling (always present, minor annoyance)

Claude.exe (PID 29504) runs git status --porcelain every 10 seconds. Each spawns git.execonhost.exe (-ForceV1) → git.exe (mingw64). This is 3 processes × 6/min = ~18 console windows per minute.

2. Session/dialog start burst (the real problem — ~20 windows at once)

When a new session or conversation starts, Claude fires a burst of 10-15 processes within ~500ms:

0:08:37,019  git status --porcelain
0:08:37,034  git --version
0:08:37,058  git rev-parse --verify HEAD
0:08:37,182  gh auth status --hostname github.com
0:08:37,293  git rev-parse --verify origin/main
0:08:37,372  git merge-base origin/main HEAD
0:08:37,446  git rev-list --left-right --count origin/main...HEAD
0:08:37,516  git diff --numstat -M <commit>
0:08:37,524  git diff --no-ext-diff -M --no-color <commit>
0:08:37,532  git diff --name-status -M <commit>

Then seconds later:

0:08:58,336  claude.exe plugin marketplace list --json
0:08:58,463  git status --porcelain
0:08:58,512  gh auth status --hostname github.com
0:08:59,452  claude.exe plugin marketplace update claude-plugins-official

And from a child Claude.exe (PID 40412):

0:09:25,440  cmd.exe /c "python --version"
0:09:25,557  cmd.exe /c "python --version"
0:09:25,605  cmd.exe /c "python --version"
0:09:25,664  cmd.exe /c "python --version"
0:09:25,703  cmd.exe /c "python --version"

python --version checks in 260ms!

Summary

| Pattern | Frequency | Processes per burst | Visual impact |
|---------|-----------|-------------------|---------------|
| git status polling | Every 10s | 3 (git→conhost→git) | 1 flash |
| Session start | On new dialog | ~15-20 | ~20 simultaneous flashes |
| Plugin checks | On session start | 5-7 | 5-7 flashes |
| Python version probe | On session start | 5 | 5 flashes |

Important: disabling all hooks does NOT fix this

I disabled all project hooks (SessionStart, PreToolUse, PostToolUse) and all plugin hooks (hookify, context-mode, security-guidance). The flashing persists — it's caused by Claude Code's own internal subprocess spawns, not by user hooks.

Suggested priorities

  1. All internal spawn()/execFile() calls for git, gh, python, and claude.exe subprocesses need windowsHide: true (or CREATE_NO_WINDOW)
  2. The python --version probe running 5 times is likely a bug — should be cached after first check
  3. Consider batching the session-start git queries or running them sequentially to reduce the simultaneous window count
Elie-Simard · 5 months ago

Still experiencing this on v2.1.87 (Windows 11, Git Bash shell).

Impact: Every hook execution (PreToolUse, PostToolUse, Stop, SessionStart) spawns node.exe via child_process without windowsHide: true, causing a visible terminal flash. MCP stdio servers using npx also flash at session start.

What we've done to mitigate (but can't fully fix):

  • Consolidated 7 hooks into 2 unified scripts (~60% fewer spawns per tool call)
  • Removed Read from PreToolUse matcher (~50% fewer hook invocations)
  • Merged 3 Stop hooks into 1 (sound + dream + kanban in one script)
  • Pre-installed MCP packages globally to reduce npx download time
  • Removed unused MCP servers

What's still flashing: Every remaining hook spawn (2 per Edit/Write/Bash, 1 per Stop, 1 per SessionStart) and MCP stdio server connections at boot.

The fix we need: windowsHide: true on all child_process.spawn() calls for hooks and MCP stdio servers on Windows. This is a one-line fix per spawn call.

dorian-simes · 4 months ago

Reproducing this on Windows 11 Pro (10.0.26200) with Claude Code in VS Code. Git Bash windows flash on every tool call (Bash, Grep, Glob, etc.). Using the VS Code extension, not standalone CLI. Very disruptive during normal use.

Captnwalker1 · 4 months ago

This is extremely frustrating, makes using claude so much more time consuming. More often than not the console windows close themselves, but if you have a custom statusline configured this one will stay open usually, and sometimes seems to prevent commands from finishing.

pauljones0 · 4 months ago

Hey want to crash my laptop? Why don't you introduce a bug that creates 150+ terminal windows? Great.
Is Anthropic going to fix it? No.
Can I use a non-claude application? No because the twerps running this gong show are worried about overuse.
They're going to keep vibe coding their lil dumb application. I hope this bubble pops.

Captnwalker1 · 4 months ago

This ended up being a issue with using git-bash.exe being used for SHELL, changed in ~/.claude/settings.json and now no issue

{
  "env": {
    "SHELL": "C:\\Users\\YOURUSER\\AppData\\Local\\Programs\\Git\\usr\\bin\\bash.exe",
    "CLAUDE_CODE_GIT_BASH_PATH": "C:\\Users\\YOURUSER\\AppData\\Local\\Programs\\Git\\usr\\bin\\bash.exe"
  },
Niizuki · 4 months ago

Seeing a more severe variant of this on Claude Code 2.1.68 / Windows 11
Enterprise 10.0.26200 with Git for Windows bash ($SHELL=/bin/bash.exe).

The Git Bash (MINGW64) window does not just flash — it opens and
stays open with an empty interactive prompt, and Claude Code is blocked
from continuing until I manually close the window. It happens intermittently
during normal Bash tool calls; no specific command reliably triggers it.

No user-configured hooks in settings.json. Confirmed these do not fix it:

  • git config --global core.pager cat
  • git config --global credential.helper manager

Hypothesis: on Windows the Bash tool is occasionally spawning
git-bash.exe / mintty.exe with a visible console attached instead of
hidden. Spawning child processes with CREATE_NO_WINDOW (or equivalent
detached mode) should prevent any visible terminal from ever surfacing.

Lonli-Lokli · 4 months ago

If I understand correctly, we have to wait until somebody from Anthropic vibe coding team started to use windows

towerbrother · 3 months ago

Still seeing the flash after setting SHELL and CLAUDE_CODE_GIT_BASH_PATH in ~/.claude/settings.json. Running Git Bash (mintty) on Windows 11, Claude Code v2.1.x.

marcodelpin · 3 months ago

Architectural framing + open-source workaround (5-month flash-source cascade analysis)

Confirming this is still active on 2.1.145 (Windows 11 26200). I've spent ~3 months reverse-engineering the spawn topology and shipping a complete workaround stack. Sharing the architecture in case it helps prioritize the upstream fix.

Root cause is broader than windowsHide:true

The hook dispatch pipeline wraps EVERY hook command in bash -c "..." even when the target IS an .exe. Example from settings.json:

"command": "$HOME/.claude/hooks/bin/my-hook.exe --arg"

This gets executed as bash.exe -c "$HOME/.claude/hooks/bin/my-hook.exe --arg"bash.exe is CUI subsystem → conhost.exe flash, even if my-hook.exe is itself GUI subsystem. The bash wrapper is the visible window source for hook commands, NOT the hook executable.

Proposed fix (architectural, not windowsHide: flag tweak):

When the hook command's argv[0] resolves to an .exe directly (no shell metachars in argv), invoke it via child_process.spawn(<exe>, [<args>], { windowsHide: true, ... }) directly, bypassing the bash wrapper. This eliminates the bash.exe → conhost.exe flash entirely for native-binary hooks.

For Bash tool calls themselves (genuine shell scripts), windowsHide:true on the bash spawn AND on all child spawns (taskkill cleanup, ps inspection) would address the residual flashes — see related closed dups #43844, #59040, and #19012.

Open-source workaround stack we deployed

Working solution shipping CUI-flash from ~155/min to ~33/min:

  1. GUI-subsystem bash.exe trampoline — wraps bash.exe invocations. Detects single-token shell-metachar-free commands and bypasses bash entirely via exec.Command(target_exe, args...). Falls back to ConPTY for legitimate shell commands. Critical fix: HOME="" (empty string set by claude.exe in 163/164 invocations observed) requires fallback to USERPROFILE — otherwise ~/<path> tokens don't expand and bypass silently fails.
  1. GUI-subsystem launcher pattern for any locally-deployed CUI binary called from hooks: a small Go (or equivalent) shim sets syscall.SysProcAttr{HideWindow: true} on the exec.CommandSTARTF_USESHOWWINDOW + SW_HIDE ensures conhost.exe is allocated but never shown. Output flows normally via inherited stdin/stdout/stderr handles.
  1. Custom hook client replacing direct hook commands in settings.json. ConPTY-based with autospawn-grace + dial-with-retry. Eliminates ~all hook-event conhost flashes.
  1. PATH shim layer at ~/.claude/shims/: GUI-subsystem trampolines for git.exe, cmd.exe etc., prepended to PATH for sub-processes spawned by hook handlers. Catches the cascading flash from sub-tools that internally invoke other CUI binaries.

Result on a 6-repo VS Code workspace + active hook dispatch: 0 visible conhost windows observed under sustained Bash tool load + statusline polling.

Cofactors NOT addressed by user-side workarounds

Two claude.exe (node) internal spawn patterns bypass any user-side trampoline because they invoke cmd.exe via absolute C:\WINDOWS\system32\cmd.exe path:

  • cmd.exe /d /s /c "taskkill /pid <X> /T /F" — 14 flash/min (Bash tool subprocess cleanup)
  • cmd.exe /d /s /c "ps -axo user,pid,stat,command" — 5.6 flash/min (process inspection, UNIX command on cmd.exe = always fails but flash happens first)

Combined ~20 flash/min purely from claude.exe internal subprocess inspection/cleanup that no user-side shim can intercept. These would benefit from:

  • taskkill: replace with native Win32 TerminateJobObject (no cmd.exe)
  • ps -axo: replace with NtQuerySystemInformation or Node process APIs (no cmd.exe needed; the cmd.exe call ALWAYS fails because ps is UNIX-only — so the data isn't even being collected)

Asks

  • Could Anthropic team comment on whether this is on the roadmap? 5 months silence is the main pain — even a "tracked internally, won't fix" answer would let community deprioritize workaround maintenance.
  • The .exe direct-invoke architectural change has higher ROI than windowsHide because it eliminates the wrapper bash process entirely (lower latency too: ~10ms/hook saved per skipped bash -c startup × hooks-per-turn = noticeable on long sessions).
JustinCMR · 3 months ago

New reproduction path: clicking "New Session" in Claude Desktop triggers a ~50-spawn cascade with ~30 visible conhosts

Adding evidence for a different trigger than the statusline/hook scenarios already documented: the "New Session" button itself.

Repro

  1. Have Claude Desktop open with at least one git project
  2. Click New Session in the live Claude Code window (don't even send a message)
  3. Watch the screen: ~30 brief console-window flashes over ~75 seconds

What's happening (process snipe, 60ms WMI polling)

Captured the full cascade from a single click. ~50 subprocesses spawn, parent = Claude Desktop main (Electron) process. Timeline of key events after click:

| Δt from click | Subprocess | conhost |
|---|---|---|
| t+0s | git fetch --prune origin | VISIBLE |
| t+0s | git credential-manager get + store | inherited |
| t+5s | git index-pack --stdin --fix-thin | inherited |
| t+16s | git maintenance run --auto (auto-triggered by fetch) | inherited |
| t+17–27s | git repack -d -l --geometric=2 --write-midx, git pack-objects, git multi-pack-index writemulti-second console ops | inherited |
| t+18s | gh pr view <branch>, gh pr checks <num>, gh pr view <num> (PR context lookup) | VISIBLE ×3 |
| t+30s | git update-ref refs/heads/main (fast-forward local main) | VISIBLE |
| t+32s | git worktree add --no-checkout -b claude/<auto-name> <user-path>\.claude\worktrees\<auto-name> | VISIBLE |
| t+32s | git checkout HEAD -- . :(exclude).claude, git config extensions.worktreeConfig true | VISIBLE ×2 |
| t+36s | Same gh pr view <branch> as t+18s (redundant query) | VISIBLE |
| t+40s | New claude.exe (claude-code child) spawns for the session | VISIBLE |
| t+41s | Session init: git --no-optional-locks status --short, log --oneline -n 5, config user.name, config user.email | VISIBLE ×4 |
| t+41s | cmd.exe /d /s /c "git config --get user.email" (raw cmd.exe wrapper) | VISIBLE |
| t+41s | cmd.exe /d /s /c "tasklist \| findstr /I \"Code.exe Cursor.exe Windsurf.exe idea64.exe pycharm64.exe ...\"" — IDE detection | VISIBLE |
| t+41s | powershell.exe -NoProfile -Command "(Get-CimInstance Win32_Process -Filter ...).CreationDate.Ticks" | VISIBLE |

Smoking gun: the spawn code knows how to do --headless, just doesn't use it consistently

Over the same 90-second window, the SAME parent (Claude Desktop main process) spawned ~30 subprocesses with auto-allocated console (no --headless flag — Windows wrote conhost.exe 0x4) and exactly 1 with proper conhost.exe --headless --width 80 --height 24 --signal 0xNNNN --server 0xNNNN.

So the bug isn't "Electron can't hide console windows on Windows" — it's that the spawn path for git/gh/cmd/powershell tool subprocesses is missing windowsHide: true (or CREATE_NO_WINDOW), while the spawn path for some Electron-internal utility processes correctly sets it.

Compounding factors observed in the same investigation

  1. Session restore after auto-update: same cascade fires for each claude-code session being restored. Captured 1.9255.0.0 → 1.9255.2.0 update auto-restoring 2 sessions → 2 visible-conhost spawns + downstream chain.
  1. git maintenance run --auto is the worst offendergit repack + git pack-objects are seconds-long console processes. A typical click-spawn flash lasts ~50ms; these last seconds.
  1. Redundant PR queries: gh pr view <branch> runs twice during a single new-session init (visible at t+18s and t+36s in the table). Halving these would halve the gh-related flashes.
  1. Compounds with #62659: in the same investigation, found 6 orphan bash.exe processes (one alive 13 hours) that survived the originating claude-code session's death — generating ~8 gh.exe spawns/sec via a model-generated until ... do true; done busy-wait loop. Each spawn flashed because of this issue (#14828). Without the visible-conhost bug, the orphan loop would silently waste CPU; with it, the loop becomes a focus-stealing keystroke killer.

Environment

  • Claude Desktop: 1.9255.2.0 (Microsoft Store install)
  • claude-code: 2.1.149
  • Windows 11
  • Git for Windows (standard install)
  • GitHub CLI installed
theotillotson · 2 months ago

Independent confirmation on Claude Code 2.1.142 / Windows 11 (build 26200).

@JustinCMR's diagnosis matches what I see — the tool-subprocess spawn path is missing windowsHide: true / CREATE_NO_WINDOW, and the windowless path already exists in the codebase: a live process snapshot shows conhost.exe --headless --width 80 --height 24 --server 0x... being used for some child processes but not the shell/tool ones. So this is a one-flag inconsistency, not a platform limitation.

One spawn path I haven't seen called out yet: MCP stdio servers. Each configured MCP server is launched as a child node.exe / python.exe at session start/restore, and those spawns also lack windowsHide — so they flash too. For anyone running several MCP servers, that's an extra burst of windows on every session start, on top of the per-tool-call flashes. Whatever fixes the tool-subprocess path should cover the MCP stdio launch path as well (same child_process.spawn options).

Mitigation for the portion that's user-owned (not the harness): if some of your flashes come from your own Windows automations (scheduled tasks, file watchers, helper scripts) rather than Claude Code itself, that part is fixable today:

  • Scheduled tasks: add -WindowStyle Hidden to a powershell.exe action, or switch a console python.exe action to pythonw.exe.
  • On-demand launches: wrap in a one-line wscript VBS shim — CreateObject("WScript.Shell").Run cmd, 0, False — which gives zero flash (window style 0), stronger than -WindowStyle Hidden (which can still flash briefly).

I verified this silences recurring scheduled automations completely. It does not touch the Claude Code tool/MCP flashes (those still need the windowsHide fix above), but it removed a meaningful chunk of the noise in my case.

+1 for prioritizing the one-flag fix — this is a daily, focus-stealing problem on Windows.

AgathoZorya · 2 months ago

Same root cause, confirming with an independent diagnostic method — also affects Windows PowerShell tool calls, not just Bash. Full write-up and a feature suggestion posted on #58606 (which has the closest match in scope), copying the key part here since this issue is older:

Environment: Windows 11 Home, build 10.0.26200, anthropic.claude-code v2.1.181-win32-x64, Git Bash + Windows PowerShell 5.1.

I confirmed the bash.exe/powershell.execonhost.exe (no CREATE_NO_WINDOW) pattern via Windows native Process Creation auditing (Event ID 4688 in the Security log) while debugging an unrelated, separate console-flash issue on the same machine. That gave a reliable way to attribute flashes specifically to Claude Code's tool execution versus anything else running on the box.

Suggestion: rather than only hiding the window, consider exposing it as a setting (hidden / visible / visible-signed with a distinguishing title) — some users may want the visibility as a "Claude is working" signal rather than have it fully suppressed. Details on #58606.

Geozstevenzz · 2 months ago

This very annoying

AgathoZorya · 2 months ago

claude code made an "visible-signed with a distinguishing title windows" for my powershell process -
so now there is no more flash on my computer - its very comfortable one ..

AgathoZorya · 2 months ago

Can confirm - traced this with Windows Process Creation auditing (Event ID 4688): every Bash/PowerShell tool call from Claude Code spawns bash.exe/powershell.exe -> conhost.exe without CREATE_NO_WINDOW. Seems to be the extension host launching child processes without the no-window flag, not something fixable from the user side (settings.json hooks etc. don't touch this path). Hoping for an exposed window-style option on the tool execution side.

AgathoZorya · 2 months ago

For anyone else dealing with console flashes from their own elevated PowerShell calls (separate from the tool-execution flash above) - a small trick that helped distinguish "my own elevated action" from "something else is flashing":

The problem

Start-Process ... -Verb RunAs opens a real console window for the elevated command. By default it opens and closes very fast (a flash) and gives no clue about who launched it.

The fix: title + visible banner + wait

Start-Process powershell -Verb RunAs -ArgumentList '-NoProfile -ExecutionPolicy Bypass -Command "$host.UI.RawUI.WindowTitle=''MYTOOL - diagnostic''; Write-Host ''=== MYTOOL - action in progress ==='' -ForegroundColor Cyan; & \"script.ps1\""' -Wait
  • $host.UI.RawUI.WindowTitle = '...' renames the title bar - visible on Alt+Tab / taskbar
  • Write-Host '=== ... ===' -ForegroundColor Cyan prints a colored banner right when the console opens, readable even on a quick screenshot
  • -Wait keeps the window open for the whole duration of the action instead of a few-millisecond round trip

Result: a flash with a UAC prompt + a clearly signed window is unambiguous (a deliberate elevated action), versus a flash without UAC and without a signature, which is worth investigating as coming from something else entirely (in my case, two unrelated background services that never trigger UAC turned out to be the real source of separate, unexplained flashes).

khaledfouad0 · 2 months ago

Still reproduces on v2.1.195 (Windows 11, Git Bash backend, VS Code integrated terminal).

Confirmed via process monitor — the flash is claude.exe spawning the hook/statusLine subprocess without a hidden-window flag, so Windows allocates a conhost.exe per spawn. Representative chain:

conhost.exe ← bash.exe ← claude.exe   (e.g. bash -c 'sh -c "exec sh .../statusline.sh"')

Two distinct triggers on this build:

  • statusLinebash → jq → bunx re-spawned on every render (continuous flicker even at idle). Same as the older #51867.
  • Hook subprocesses — any type:command hook is wrapped in an un-hidden bash -c, firing on every PreToolUse/PostToolUse/UserPromptSubmit. Plugin hooks that use bash/python3 (e.g. PostToolUse-on-every-tool-call) flash on each tool use.

windowsHide: true in hook config and CREATE_NO_WINDOW in the env block are both ignored — the flash is claude.exe's own outer wrapper, before the hook command runs, so neither has any effect (matches #27115).

Mitigations that actually reduce it (not eliminate): disabling hook-heavy plugins and clearing the custom statusLine. Only WSL gets it to zero. A CREATE_NO_WINDOW / STARTF_USESHOWWINDOW+SW_HIDE flag on the internal spawn is the real fix.

Eddie-Huang · 2 months ago

Install RTK (https://github.com/rtk-ai/rtk) has solved this annoying shit!! At least for Bash tool, Search still flash that cmd window

romemozu · 1 month ago

Same issue here, and it makes Claude Code very hard to use for non-stop daily work on Windows 11 (v2.1.197, PowerShell host).

Extra detail from monitoring process creation events (Win32_Process __InstanceCreationEvent) that may help pinpoint it. Visible conhost/console windows flash on EVERY turn from Claude Code's own internal spawns, not just user tool calls:

  • git.exe --no-optional-locks status --short and git log --oneline -n 5 (parent = claude.exe)
  • gh.exe pr view <branch> --json ... (parent = claude.exe)
  • cmd.exe /c tasklist | findstr /I "Code.exe Cursor.exe ..." (IDE detection, parent = claude.exe)
  • one visible bash.exe window per configured hook (SessionStart / PostToolUse / Stop)
  • MCP stdio servers (node.exe) launched with a visible console window

Workarounds we applied (consolidating hooks into one orchestrator per event, wrapping MCP servers in a pythonw launcher with STARTUPINFO+SW_HIDE and a kill-on-close Job Object, switching the default terminal from Windows Terminal to Console Host) reduce it a lot, but the internal git/gh/tasklist spawns cannot be hidden from user side at all.

Feature request: spawn internal child processes and hook/MCP commands with CREATE_NO_WINDOW (or a settings.json option like "hideChildConsoles": true) on Windows.

Captnwalker1 · 1 month ago
Same issue here, and it makes Claude Code very hard to use for non-stop daily work on Windows 11 (v2.1.197, PowerShell host). Extra detail from monitoring process creation events (Win32_Process __InstanceCreationEvent) that may help pinpoint it. Visible conhost/console windows flash on EVERY turn from Claude Code's own internal spawns, not just user tool calls: git.exe --no-optional-locks status --short and git log --oneline -n 5 (parent = claude.exe) gh.exe pr view <branch> --json ... (parent = claude.exe) cmd.exe /c tasklist | findstr /I "Code.exe Cursor.exe ..." (IDE detection, parent = claude.exe) one visible bash.exe window per configured hook (SessionStart / PostToolUse / Stop) * MCP stdio servers (node.exe) launched with a visible console window Workarounds we applied (consolidating hooks into one orchestrator per event, wrapping MCP servers in a pythonw launcher with STARTUPINFO+SW_HIDE and a kill-on-close Job Object, switching the default terminal from Windows Terminal to Console Host) reduce it a lot, but the internal git/gh/tasklist spawns cannot be hidden from user side at all. Feature request: spawn internal child processes and hook/MCP commands with CREATE_NO_WINDOW (or a settings.json option like "hideChildConsoles": true) on Windows.

curious if you tried my solution above? https://github.com/anthropics/claude-code/issues/14828#issuecomment-4253966091

feelgoodfarms · 1 month ago

Adding a data point + a diagnostic method, in case it helps others triage.

On a Windows box with a lot of automation, I had the same unbearable flashing — dozens of PowerShell/conhost/OCR windows per second stealing focus. When I actually traced what was spawning each window, most of my storm was not Claude Code's tool calls. The two dominant sources were:

  1. My own Scheduled Tasks registered with LogonType = Interactive ("Run only when user is logged on"). Those run in the visible desktop session (session 1), so every console child they spawn — PowerShell, cmd, and especially tesseract.exe / pdftoppm.exe in OCR pipelines (one window per page) — flashes. The task's "Hidden" checkbox and -WindowStyle Hidden are cosmetic and do not stop this.
  2. A third-party RGB/fan-control utility polling the GPU by launching an un-hidden PowerShell every few seconds.

Diagnostic: attribute each flash to its real source

Run in an elevated PowerShell — this shows console processes in your visible session and their parent, so you can see whether Claude Code, a Scheduled Task (svchost … Schedule), or some app is the spawner:

$s = (Get-Process -Id (Get-CimInstance Win32_Process -Filter "Name='explorer.exe'").ProcessId).SessionId
Get-CimInstance Win32_Process -Filter "Name='powershell.exe' OR Name='cmd.exe' OR Name='tesseract.exe' OR Name='pdftoppm.exe' OR Name='conhost.exe'" |
  Where-Object { (Get-Process -Id $_.ProcessId -EA SilentlyContinue).SessionId -eq $s } |
  ForEach-Object { $p=(Get-CimInstance Win32_Process -Filter "ProcessId=$($_.ParentProcessId)").Name; "$($_.Name) <- $p" } |
  Group-Object | Sort-Object Count -Descending

The fix for the non-Claude sources

The key realization: what decides visibility is the session, not the "Hidden" flag. Interactive logon = session 1 = visible; SYSTEM/ServiceAccount (or "Run whether user is logged on or not") = session 0 = invisible.

  • Back up first: Export-ScheduledTask every interactive task to XML so it's reversible.
  • Convert the offending tasks to run in session 0:

``powershell
$p = New-ScheduledTaskPrincipal -UserId 'SYSTEM' -LogonType ServiceAccount -RunLevel Highest
Set-ScheduledTask -TaskName 'YOUR_TASK' -Principal $p
``

  • Caveat: tasks that read Credential Manager / DPAPI secrets or a browser profile must not go to SYSTEM (they'll run but fail auth) — keep those as your user via "Run whether user is logged on or not" (Password logon), which is also session 0. Note S4U cannot be set for a different user by SYSTEM (Access denied 0x80070005).
  • For Python pipelines shelling out to CLI tools, pass creationflags=subprocess.CREATE_NO_WINDOW on Windows (common with pytesseract/pdftoppm/ffmpeg).

Eliminating those two sources removed the overwhelming majority of my flashing. The remaining Claude-Code tool-spawn flashes are what this issue is about — this isn't a fix for that, just a way to rule out (and kill) the other contributors so you can see how much is actually the tool spawns.

magicexperience · 1 month ago

I fully agree. I hope this gets fixed soon because it's really disruptive when trying to work.

NiceLeader · 1 month ago

Building on the tracing approaches above (@romemozu's process-creation events, @feelgoodfarms' method): before assuming every flash is Claude Code's missing windowsHide, it is worth separating two sources - Claude Code's own spawns vs plugin hook configs launched through a visible shell (claude-mem-class "shell": "bash" entries, login-shell -l flags). The second kind is fixable today.

I packaged the whole triage-and-fix loop I ended up building: a window sampler (logs class + owning process + command line of every new visible window while you reproduce), a scanner for the known flash-causing hook shapes across Claude Code / Codex / Cursor plugin dirs, and a fixer that wraps offenders in a locally-compiled GUI-subsystem launcher (the only spawn that cannot flash - powershell -WindowStyle Hidden provably still flashes on Win11 + Windows Terminal before it hides): https://github.com/NiceLeader/agent-hooks-doctor

On my machine that eliminated the majority of the flashing; what remains is genuinely this issue and needs the one-line windowsHide: true upstream.

ayer-ribeiro · 1 month ago

Adding a data point that might help others hit this: my CLAUDE_CODE_GIT_BASH_PATH was pointing at git-bash.exe (the GUI/mintty launcher) instead of the actual console binary. git-bash.exe is a windowed app by design, not a console-subsystem process, so no windowsHide/CREATE_NO_WINDOW flag Claude Code passes internally can suppress it. It's always going to draw a window no matter what.

Pointing the env var at the real binary instead fixed it completely for me:

CLAUDE_CODE_GIT_BASH_PATH=C:\src\Git\usr\bin\bash.exe

(path will vary depending on where Git for Windows is installed; look for usr\bin\bash.exe, not git-bash.exe/git-cmd.exe)

No flashing on any Bash tool calls after restarting Claude Code so the new env var takes effect.

This is basically the same idea @DamianReeves posted above (pointing SHELL at a real bash.exe rather than a wrapper), and matches the CLAUDE_CODE_GIT_BASH_PATH workaround @ilude listed in the consolidated writeup. Worth double-checking if you have either var set at all, make sure it resolves to bash.exe, not a GUI launcher.

Environment: Windows 11, Claude Code CLI v2.1.81 (npm install), Git installed at a custom path (not the default Program Files\Git).

NiceLeader · 1 month ago

@ayer-ribeiro great catch - that is a genuinely distinct shape: git-bash.exe is the mintty GUI launcher, a windowed app by design, so no windowsHide/CREATE_NO_WINDOW the runtime passes can ever suppress it. Nothing upstream can fix that one; only the config can.

Added detection for exactly this misconfig to the doctor I linked above - it now flags CLAUDE_CODE_GIT_BASH_PATH pointing at git-bash.exe (env var or settings.json) and suggests the console binary path (...\bin\bash.exe), credited to you in the README.

LarryThiessen · 1 month ago

Still present in 2.1.220, and it reached a path that was previously quiet for me: the statusLine command.

What changed: my statusLine in ~/.claude/settings.json has run powershell -ExecutionPolicy Bypass -File …statusline.ps1 unchanged since 2026-07-23, with no visible window. After the auto-update 2.1.219 → 2.1.220 (2026-07-30 05:36 UTC) the same command flashes a console window on every statusline refresh — i.e. after every message and tool call, so it's near-constant.

Nothing local changed: settings.json mtime 2026-07-23, hook scripts mtime 2026-06-26. The CLI update was the only change on the machine, and the flashing started the same day.

Environment: Windows 11 Home 26200 · Claude Code 2.1.220 (npm-global) · Node v24.15.0 · statusLine type command.

If it helps narrow the spawn site: the SessionStart/UserPromptSubmit hooks in the same settings file run node rather than powershell. I'm switching the statusline to node as a workaround and can report back whether that path flashes too.

LarryThiessen · 1 month ago

Correction / broader than my last comment: on this machine the flashing is not limited to the statusLine path. Every Bash/PowerShell tool call in 2.1.220 flashes a console window too — confirmed by watching it fire in lockstep with a gh invocation.

This user saw zero window flashing on 2.1.219 and earlier — both the tool-spawn and statusline paths were silent — so for at least some Windows setups 2.1.220 looks like a regression in subprocess window-hiding generally, rather than the long-standing behaviour described in the original report.

LarryThiessen · 1 month ago

Retracting my two earlier comments in this thread — the attribution was wrong and I don't want it left as bad data in a bug report.

I claimed console-window flashing appeared with the 2.1.219 → 2.1.220 update. That does not hold:

  • This machine runs Claude Code via the desktop app, which bundles its own copy. Only 2.1.217 and 2.1.219 were ever installed for it, and every live process was 2.1.219. The 2.1.220 update I cited was a separate npm-global install that the app never ran — I read its version from a terminal and wrongly attributed it to the app.
  • The actual cause was local and unrelated to Claude Code: a Windows Task Scheduler job (a 10-minute uptime check invoking powershell.exe … -WindowStyle Hidden). Watching the process tree tied each flash to that task's exact run time, to the second.

So this environment is not evidence of a 2.1.220 regression, and the version boundary I described should be disregarded. Sorry for the noise.

One incidental note that may still be useful to others debugging this class of problem: -WindowStyle Hidden on powershell.exe does not prevent the flash, because Windows allocates the console host before PowerShell can apply the window style. A scheduled task has to run non-interactively (S4U) to be genuinely windowless.

sorenssl · 1 month ago

Still present in 2.1.207 (VSCode extension, Windows 11 Home 10.0.26200, Git Bash / MSYSTEM=MINGW64).

Adding two things I did not see covered in the thread: an explicit elimination of my own software as the source, and a list of the configuration paths that do not work around it.

Ruled out my own software first

My property surveillance service was spawning ffmpeg subprocesses without CREATE_NO_WINDOW. Fixing that removed 4 persistent windows — and the flashing from tool calls remained, in lockstep with each Bash/PowerShell invocation. So on this machine the remaining windows are demonstrably Claude Code's own spawns, not a third-party process.

Worth stating because "check your own subprocesses" is a reasonable first response, and here it was genuinely part of the picture — just not all of it.

Configuration paths that do not work around it

| Attempted | Why it does not help |
|---|---|
| env in settings.json | sets environment variables, not process creation flags |
| switching shell (Git Bash → pwsh/cmd) | the issue is in how the process is spawned, not what is spawned |
| Windows Terminal default profile | not involved — bash.exe is spawned directly, never via Windows Terminal |
| --allowed-tools / --disallowed-tools | controls which tools may run, not spawn flags |
| hooks | cannot influence the caller's spawn flags |

Why this scales badly with agentic use

The impact is not the window itself, it is the keyboard focus theft. Focus is pulled away mid-word while typing, once per tool call. A single long autonomous session runs to several hundred tool calls.

That inverts the value of agentic mode: the more independent the agent is, the less usable the machine becomes while it works. Concretely, I am building a camera surveillance system (6 cameras, YOLO detection, ~175 automated tests) interactively with Claude Code, and long unattended runs are exactly the workflow this punishes hardest.

Request

windowsHide: true on the child_process.spawn() calls — both the shell-snapshot path and hook execution, per the analysis in #64163.

Happy to test a pre-release build on this setup if that is useful.

cyneta · 1 month ago

Adding a diagnosed reproduction from daily heavy use, in case it helps prioritize.

Environment: Windows 11 Pro (10.0.26200), Claude Code v2.1.212 native (not WSL), Git Bash installed.

Setup that triggers it: statusLine is "type": "command" invoking bash ~/.claude/scripts/statusline-command.sh, plus several hooks (PreToolUse/PostToolUse/Stop/PreCompact) each invoking bash ~/.claude/scripts/*.sh. Around 13 concurrent Claude Code sessions open across VS Code windows.

Observed: a black console window flashes on screen every few minutes, even when all sessions are idle. It is fast enough that no title bar is readable, which made it hard to attribute (we first chased an unrelated third-party scheduled task).

Evidence: a 2-minute process-launch poller (Win32_Process snapshot diff at 150 ms) caught the spawn chains repeatedly:

claude.exe -> bash.exe -c "bash ~/.claude/scripts/statusline-command.sh" -> conhost.exe
claude.exe -> bash.exe -c "bash ~/.claude/scripts/handoff-breadcrumb.sh"  -> conhost.exe

Three such chains in one 2-minute window from a single session; with 13 sessions the statusline refreshes alone produce a visible conhost flash every few minutes. We also observed 25 lingering hidden conhost.exe processes owned by these chains.

Impact: constant focus-stealing flashes during normal work; also makes users suspect malware (an anonymous black window firing every few minutes is indistinguishable from one at a glance).

Ask: pass windowsHide: true (and ideally CREATE_NO_WINDOW) on the child_process.spawn calls used for statusLine commands and hook commands on win32. Happy to test a build.

cyneta · 1 month ago

Correction / retraction of my comment above.

On closer measurement, I was wrong to attribute the flashing to the statusLine and hook path on this version. Re-testing on v2.1.212 native (Windows 11):

  • The bundled runtime's statusLine/hook spawner does pass windowsHide: true on this build - all three spawn forms, including the statusLine path (spawn(cmd, [], {..., windowsHide: true})).
  • Empirically: an EnumWindows watcher polling every 30-40 ms over 1191 statusLine bash.exe spawns in ~50 s (13 concurrent sessions) recorded zero new visible top-level windows of any class (ConsoleWindowClass / PseudoConsoleWindow). conhost.exe processes are created but no window ever becomes visible.

So on 2.1.212 the statusLine/hooks are not the source of the visible flashing, contrary to my earlier comment.

The console spawns I can still see being created are stdio MCP servers, launched as:

C:\Windows\system32\cmd.exe /d /s /c "npx @playwright/mcp@latest"

(grandparent claude.exe, allocating a fresh console 0x4). If anyone is still getting flashes on a current build, worth checking whether the windowsHide flag is being passed on the MCP stdio server spawn path specifically, as opposed to the statusLine/tool paths - they appear to be separate spawn sites. Repro is trivial: configure any stdio MCP server whose command is npx (so it resolves through cmd.exe) and watch for a console flash each time the server boots/reconnects.

Happy to test a build with windowsHide on the MCP spawn path.

godefroi · 29 days ago

All the diagnostics I've done point to the statusline. I installed ccstatusline, got flashing windows; replaced ccstatusline with a custom powershell script, still get flashing windows. Disabled all my stdio MCPs (had Aspire and cwm-roslyn-navigator), still got flashing windows. Disabled my custom statusline altogether, and no more flashing windows.

JuanjoB-Viewnext · 27 days ago

Adding a data point that isolates this to Claude Code CLI's spawn of hook commands, not the plugin/tool being invoked.

What we ruled out downstream

I opened alexgreensh/token-optimizer#104 about the same flashing during normal work on Windows and tried three progressively more aggressive workarounds inside the plugin:

  1. VBS wscript //B wrapper around every hook command — no visible reduction
  2. pythonw.exe preference in the plugin's own launcher (later landed upstream by the plugin author as _maybe_swap_to_pythonw) — no visible reduction either
  3. Skipping the intermediate bash.exe entirely by rewriting hooks.json to invoke pythonw run.py directly as the first token — no reduction (user reported it looked slightly worse)

After the plugin author landed v5.11.72+ with the console-safe python swap and fixed a separate console-allocating Scheduled Task in the plugin itself, the observation on my Windows box was:

  • ✅ Idle "5-minute pops" from the plugin's scheduled task: gone
  • ❌ Per-tool-call flashes during active work: unchanged

Since no downstream fix removed the per-tool-call flash, the console allocation must be happening in the cmd.exe /c "<hook command>" wrapper that Claude Code CLI uses to spawn the hook — before any bash, python, or wscript inside it can matter. This is consistent with @mattgphoto's process sampling in #104 (111 bash.exe vs only 2 cmd.exe in 28s of agentic use, but the transient cmd.exe /c still allocates a console).

Concrete ask

If Node's child_process.spawn(cmd, { shell: true, ... }) for hook execution passed windowsHide: true, the cmd.exe /c shim would still exist but its transient console wouldn't be shown. That's a one-line change on the CLI side and would eliminate the flash regardless of what the hook command does inside (bash, python, wscript, direct .exe).

The plugin author documented this as an external limit in-source in v5.11.72+ (comment in #104).

Environment

  • Windows 11 Pro 26200
  • Claude Code (VSCode extension)
  • Node child spawn on Windows via shell: true
  • Python 3.13.14 (python.org), git-bash available
  • Reproducible with any plugin that registers hooks; more visible with plugins that fire many (Token Optimizer registers ~26 hooks across lifecycle events, so this is the loudest surface but not the only one)

Happy to test a candidate build if one lands.

lnorton89 · 20 days ago

Reproduced this independently and want to share two findings that add some precision beyond "a console briefly appears" — one about why it can look like a full terminal window rather than a faint flash, and one about what's actually triggering it in a hook-heavy setup.

It's not always just a conhost.exe flash — it can be a full WindowsTerminal.exe window

If HKCU:\Console\%%Startup has DelegationConsole/DelegationTerminal set to Windows Terminal's GUID ({2EACA947-7F5F-4CFA-BA87-8F7FBEEFBE69} / {E12CFF52-A866-4C77-9A90-F570A7AA2C6B}) — the default on many Windows 11 setups — then any unhidden console-allocating child process gets delegated to a brand-new WindowsTerminal.exe -Embedding process, not just a bare conhost. That's a much more visually disruptive and confusing symptom than a "flash": it reads as an entire terminal application popping up and disappearing, with its own taskbar entry, which is probably why some reports in this thread describe "a program opening" rather than a flicker.

I confirmed this is really happening (not just theorized) by polling window visibility directly via the Win32 API rather than only watching for process creation:

Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;
public class Win32 {
    [DllImport("user32.dll")]
    public static extern bool IsWindowVisible(IntPtr hWnd);
}
"@
# poll Get-Process | Where ProcessName -match 'cmd|powershell|conhost|WindowsTerminal|OpenConsole'
# and check [Win32]::IsWindowVisible($proc.MainWindowHandle) every ~40ms

This caught multiple genuinely-new WindowsTerminal.exe -Embedding processes (fresh PIDs each time, IsWindowVisible = true, non-null MainWindowTitle) appearing and disappearing within the same second-or-two window — i.e. directly-observed, not inferred from CPU/process churn.

What's triggering it: PostToolUse command hooks, compounded across concurrent sessions

Cross-referencing a WMI __InstanceCreationEvent process trace against the exact timestamps the visible windows appeared, the dominant activity at each flash was a PostToolUse hook (a command-type hook registered in settings.json, e.g. one that runs after every Bash tool call) spawning bash.exe — sometimes as a nested bash.exe -c "bash.exe ...script.sh" chain. Each spawn gets its own conhost.exe.

Two things made this much worse in my repro, which might explain why severity varies so much between reports:

  1. It scales with hook count. A single Bash-matched PostToolUse hook fires once per Bash call; a config with several matchers (Bash|Edit|Write|MultiEdit|Agent|Task etc.) on PreToolUse and PostToolUse can spawn 4–7+ child processes per single tool call.
  2. It's per-session, and sessions don't share the budget. I had two Claude Code sessions active (one Desktop-app session, one CLI-resumed session) with the same global hooks configured — both were spawning independently, so the effective flash rate roughly doubled with a second concurrent session. Anyone running multiple Claude Code windows/sessions at once (common with the Desktop app) will see this compound.

I could not pin the exact single spawn call missing CREATE_NO_WINDOW/windowsHide — dozens of hook spawns fired per minute while only a handful produced a visible window, so whatever's happening is inconsistent rather than universally broken (i.e. most hook spawns are properly hidden; something about a subset isn't). But the mechanism — hook-command spawn → occasionally-visible window, worse with more hooks and more concurrent sessions, and rendered as a full WindowsTerminal.exe window rather than a faint flash when Terminal delegation is enabled — seems like a useful, reproducible angle for narrowing down the actual missing flag.

Happy to share the full WMI trace / polling script if useful for a maintainer trying to reproduce this deterministically.

sorenssl · 20 days ago

Following up on my own comment above, and on @lnorton89's registry finding, with
a measurement of what that delegation setting actually costs.

Short version: DelegationConsole/DelegationTerminal does not just change how
the flash looks. It decides whether a lingering console host costs ~2 MB or
grows unbounded. I have ~6.5 days of process-memory sampling across both
configurations on the same machine.

First, a correction to my own table above: I listed "Windows Terminal default
profile — not involved". That entry was about which profile Windows Terminal
opens, and it stands. The console delegation setting is a different mechanism
and it turns out to matter a great deal.

1. Claude Code does leave lingering console hosts (reproducing @cyneta's "25 hidden conhost")

Measured just now:

conhost.exe children of claude.exe : 8
ages                               : 20.8, 46.3, 46.3, 46.7, 81.9, 93.0, 93.0, 93.0 h
private bytes, oldest (93.0 h)     : 1.9 MB
private bytes, youngest (20.8 h)   : 2.5 MB
all 28 conhost.exe on the machine  : 43.7 MB total

They accumulate and survive for days. On Console Host delegation this is
harmless
— the 93-hour-old one holds the same 1.9 MB as the 46-hour-old ones,
so the host itself does not grow. That is the control for what follows.

2. On Windows Terminal delegation, the host grows ~320 MB/h for as long as the console lives

Same machine, earlier, when a console-allocating child was delegated to
WindowsTerminal.exe. Sampled every ~5 min by a memory watchdog writing CSV;
recomputed from that raw CSV, two independent WindowsTerminal.exe instances:

instance 1:  38.2 h   1 223 MB -> 13 679 MB   =  326 MB/h   monotonic 100 %  (n=458)
instance 2:  12.0 h   1 541 MB ->  5 234 MB   =  308 MB/h   monotonic  98 %  (n=146)

Two separate processes, near-identical rate. Growth is time-driven, not
output-driven: split into four 6-hour bands the rate is 316 / 365 / 320 / 321
MB/h regardless of whether anything was being printed. A buffer filling with
console output would be near zero on an idle night.

Effect on system memory pressure, same CSV:

during the leak window (n=458):  median commit 77.9 %,  4 % of samples above 95 %
after the console was gone (n=1113): median commit 80.0 %,  0 % of samples above 95 %

Honest scoping — the trigger was mine, not Claude Code's. The process that
allocated that console was my own long-running service, launched with
CREATE_NO_WINDOW | DETACHED_PROCESS. DETACHED_PROCESS wins, the child gets no
console, and when it later starts a console program Windows must create a fresh
one. Dropping DETACHED_PROCESS made the WindowsTerminal.exe process disappear,
which is what pins the causality. This is the same "rule out your own software"
exercise as in my earlier comment, one layer deeper.

So I am not claiming Claude Code leaked 13 GB — its hook consoles are
transient. What I am claiming is that the two halves meet: Claude Code supplies
console hosts that linger for days (§1), and the delegation setting decides
whether such a host is a 2 MB conhost or a Windows Terminal process on a
~320 MB/h ramp (§2). On a default Windows 11 install pointing at Windows
Terminal, that combination is reachable without anyone doing anything unusual.

3. A mitigation that costs nothing while the flag question is settled

Setting the default terminal application to Windows Console Host caps the damage
when a spawn does get a visible console: a conhost flash instead of a full
WindowsTerminal.exe -Embedding window with its own taskbar entry, and the
measured growth does not occur. Current state here, with Windows Terminal
1.24.11911.0 still installed:

HKCU:\Console\%%Startup  DelegationConsole  = {B23D10C0-E52E-411E-9D5B-C09FDF709C7D}
                         DelegationTerminal = {B23D10C0-E52E-411E-9D5B-C09FDF709C7D}
WindowsTerminal.exe processes: 0        conhost.exe: 28 (43.7 MB total)

(Settings → System → For developers → Terminal, or Windows Terminal's own
"Default terminal application" dropdown.)

It is a mitigation, not a fix. It makes the missing windowsHide cheap instead of
expensive, and does nothing about the flashing itself. The request in my earlier
comment stands.

Happy to share the watchdog CSV or the sampling script if that helps anyone
reproduce this deterministically.

Environment: Windows 11 Home 10.0.26200, Claude Code 2.1.207 (VS Code extension),
3 hooks configured (one PostToolUse on Edit|Write, two UserPromptSubmit), no
custom statusLine. Sampling window 2026-08-03 → 2026-08-10, 1919 samples.

godefroi · 20 days ago

@sorenssl I can't really tell if you're positing that WindowsTerminal.exe memory usage grows unbounded as a general rule, but it's worth pointing out that on my system, it doesn't. I have one process running on my machine that has been running around 9 days and has ~174MB WS and ~616MB PM. It's currently running 5 tabs, 3 of which have claude.exe running in them.

NiceLeader · 20 days ago

Confirming the Terminal-delegation mechanism from a different angle, plus a
narrower hypothesis for why only some hook spawns leak a window.

Short recap of what I posted in July, since it is the premise here: a
console-subsystem process gets its window created VISIBLE before it can hide
itself, so powershell -WindowStyle Hidden provably still flashes on Win11 with
Terminal delegation, while a GUI-subsystem (winexe) binary spawned identically
does not.

On why it is inconsistent: nesting, not hook count

@lnorton89 notes that most hook spawns are hidden and only a subset leaks. My
data points the same way, and suggests the split is nesting.

On this machine I have two groups running side by side. Five hook entries whose
command is a single direct node "<script>" <args> have never produced a window,
across months. A plugin whose seven hook commands ran under bash with
export PATH="$($SHELL -lc 'echo $PATH' 2>/dev/null):$PATH" at the head produced
them constantly, including on PostToolUse with matcher: "*", so once per tool
call. On Windows $SHELL is /bin/bash.exe, so that prelude is a full login
shell spawned to read one variable.

That matches the bash.exe -c "bash.exe ...script.sh" chains in your WMI trace.
The plausible reading is that the runtime hides the process it starts, and the
leak is the grandchild the hook's own command line spawns, which nothing passes
CREATE_NO_WINDOW to.

If that holds, the fix is narrower than "hide all hook spawns": a hook command
that spawns its own shell is outside the runtime's control, so either the flag
has to survive into the grandchild, or the hook has to be started in a way that
cannot allocate a console at all.

Two things follow for anyone hitting this now:

  1. Grep your own hooks for nested shells before concluding the runtime is

broken. The plugin above has a fix in flight (thedotmack/claude-mem#3519)
that drops the login shell when node is already on PATH.

  1. Wrapping hook commands so they start from a GUI-subsystem process removes the

symptom regardless of which spawn is at fault, since there is no console to
create. That has held for me across four consecutive releases of that plugin,
with the hooks still executing normally.

What I cannot answer

Same as @lnorton89: I never pinned the specific spawn site missing
CREATE_NO_WINDOW / windowsHide. The launcher approach sidesteps the question
rather than answering it, so this is a mitigation, not a diagnosis. A runtime fix
is still worth having.

Unrelated latency note, since hook count came up: running several handlers in one
process instead of one process per handler is worth doing on Windows for its own
sake. Node startup is around 75 ms here, so three separate handler spawns on
every Write were adding roughly 225 ms of pre-tool latency. That is a
performance fix, not a window fix.

sorenssl · 19 days ago
Correction (later comment): the final claim below — that the leak is tied to hosting a process with no console of its own — is wrong. A missing control (phase C) showed ordinary processes behave identically. Corrected measurement here: https://github.com/anthropics/claude-code/issues/14828#issuecomment-5251012213

@godefroi Good push — and no, unbounded growth as a general rule is not what I
meant, and your 9-day instance shows it would be wrong. I ran a controlled test
overnight to find where the line actually sits. It turns out your case and mine
differ by one thing, and it is not the application.

Two phases, 75 minutes each, same machine, same night, 76 samples per phase at
60 s intervals.

PHASE A  interactive wt.exe, one idle console       76.9 -> 75.5 MB   =  -1 MB/h
PHASE B  WT as the DELEGATED console host for a     89.2 -> 485.8 MB  = 317 MB/h
         detached chain with no console of its own   96 % monotonic

Phase A behaves the way you describe: no measurable growth over 75 minutes
(the -1 MB/h is noise, not a decline), with handle count trending down,
929 -> 892.

Phase B pointed DelegationConsole/DelegationTerminal at Windows Terminal and
recreated the chain behind my original numbers: a process started with
CREATE_NO_WINDOW | DETACHED_PROCESS (so DETACHED wins and it gets no console),
which then starts a console program — forcing Windows to create a fresh console,
which delegation hands to Windows Terminal.

The two obvious objections are both ruled out by the data:

  • Not startup allocation. First third of phase B: 297 MB/h. Last third:

292 MB/h. Straight line for 75 minutes, no flattening.

  • Not console output. The payload is time.sleep(). It writes nothing at

all. This matches the original incident, where the rate was essentially
constant across all four 6-hour bands of the day — 316 / 365 / 320 / 321 MB/h
— which is not the shape an output-driven buffer produces.

So the precise claim, narrower than the one I implied: Windows Terminal does
not leak as an application — it leaks in the role of console host for a process
that has none of its own.
Same binary, two roles, and the entire difference
between them is the leak. You are looking at the first role. I was looking at
the second.

That distinction is what makes it relevant here rather than just my own bug.
Claude Code leaves console hosts parented to claude.exe alive for days — 8 on
this machine right now, aged 44 h to 116 h. Under Console Host they cost
1.9–2.5 MB each and do not grow with age: the 116-hour-old one holds 1.9 MB,
exactly what the 70-hour-old ones hold. Under Terminal delegation, that same
lingering host is on the ramp above. The registry value decides which of the
two you get.

One piece of credit I owe from my previous comment: @romemozu listed "switching
the default terminal from Windows Terminal to Console Host" among their
workarounds back on 6 July, a month before I presented the same switch as if it
were new. What I can add is why it is worth more than a cosmetic fix — the
numbers above are the difference between the two settings. Their caveat still
stands too: the internal git / gh / tasklist spawns from claude.exe
cannot be hidden from the user side at all. That fits what I see here — three
hooks configured, eight lingering hosts.

Scope, since one machine is one machine:

  • Windows 11 Home 26200, Windows Terminal 1.24.11911.0, single box.
  • I did not capture the delegated process's command line, so -Embedding is the

reasonable reading of what it was, not something I proved.

  • Phase A's process was launched by me with a profile and a tab; phase B's was

created by Windows. That is the point of the comparison, but it is more than a
one-variable difference.

And credit where it is due: your counter-example is what produced this. I had a
rate and no boundary, which is a claim rather than a finding — it took an
instance that demonstrably does not leak to turn "does Windows Terminal leak?"
into a question with an answer. One machine disagreeing with numbers was worth
more than agreement would have been.

The test is two files — a PowerShell harness that flips the setting, samples,
and restores it, plus the three-level process chain. I will post both if anyone
wants to run the same two phases elsewhere; a second machine would settle the
scope question faster than more measurements from mine.

sorenssl · 19 days ago

Correction to my previous comment.

I ended it with a claim my own test did not isolate: that Windows Terminal leaks
"in the role of console host for a process that has none of its own". The two
phases changed two variables at once — who created the Terminal process, and
whether the hosted process had a console of its own — and I attributed the whole
effect to the second.

Here is the missing control. Phase C: Terminal as the delegated console host
again, but hosting an entirely ordinary process, launched with
CREATE_NEW_CONSOLE from a parent that has its own console. No
DETACHED_PROCESS, no console-less parent anywhere in the chain.

A  interactive wt.exe, started by me                          -1 MB/h   (75 min, n=76)
B  delegated host for a DETACHED chain with no console       317 MB/h   (75 min, n=76)
C  delegated host for an ordinary CREATE_NEW_CONSOLE process 319 MB/h   (39 min, n=40)

C matches B. The DETACHED_PROCESS chain has nothing to do with it — it was
just the path I happened to find this through. The qualifier in my sentence is
wrong, and the corrected claim is both simpler and broader:

Windows Terminal grows ~320 MB/h whenever Windows makes it the delegated
console host, regardless of how the hosted process was started. It does not grow
as a terminal you opened yourself.

That makes the delegation setting the entire variable, which if anything
sharpens the practical point in this thread: any console-allocating spawn that
is not hidden lands on that path when delegation points at Terminal — including
the hook and MCP spawns being discussed here.

Two notes on method, since I got one of them wrong:

  • My first attempt at phase C measured nothing. Start-Process did not create a

new console, so no host was ever delegated. Because I logged conhost,
OpenConsole and WindowsTerminal separately with their parents, this showed up
as "no new console at all" rather than a result. Without that instrumentation
I would have reported "Windows does not delegate ordinary processes", which is
false.

  • On whether phase B's growth could be accumulation of adopted consoles rather

than a leak: the chain ran once, one Terminal PID throughout, and the
per-minute deltas show no steps — 74 intervals, mean 5.26 MB, none above 3x
the mean. A scheduled console process on this machine did fire mid-phase; that
minute grew 6.1 MB against neighbours of 5.5, 5.5, 7.5 and 4.9. Thread count
fell from 92 to 86 across the phase. Adoption would show steps and rising
threads; neither is present.

Same machine and Windows Terminal version as before (Windows 11 Home 26200,
WT 1.24.11911.0), so the scope caveat stands: one box.

godefroi · 19 days ago

@sorenssl

Correction to my previous comment.

Real talk here, drop your LLM for a second and turn on your brain. microsoft/terminal does have some reported "memory leak" issues, but every real reported issue of that type is tied to output volume, and the one you believe you've discovered here is over 2x the known worst-case output-driven scenario. If a bug like that existed, it'd be one of the most trivially reproducible, universally-triggered bugs in a massively-used, actively-staffed Microsoft repo (any detached process that allocates a console under default Windows 11 settings). It isn't filed anywhere.

marcodelpin · 18 days ago

Two independent data sets in this thread now point at the same spawn-path property, so here is a short synthesis for whoever picks this up.

  1. Mechanism (my May 20 comment): the hook dispatch pipeline wraps every hook command in bash -c "..." even when argv[0] is a plain .exe. bash.exe is a console-subsystem binary, so each dispatch materializes a conhost/Terminal window regardless of the child's own subsystem or any -WindowStyle Hidden on the inner command.
  2. Independent confirmation (@NiceLeader, Jul + Aug 10): on one machine, five hooks invoked as a single direct node "<script>" <args> never produced a window across months, while seven bash-wrapped hook commands flashed constantly. Same split, measured from the other side.

The actionable slice for maintainers is still small: when a hook command's argv resolves to a single executable with no shell metacharacters, spawn it directly with windowsHide: true instead of going through the bash wrapper; keep windowsHide on the wrapper spawn for genuine shell commands. That removes the dominant flash source for native-binary hooks without touching the general Bash-tool path.

NiceLeader · 18 days ago

I ran a controlled A/B on the wrapper question, since two readings of it were
circulating in this thread.

Setup: the same executable declared twice as a PostToolUse hook in one
settings file, differing only in declaration form. Isolated headless session
(claude -p --settings <file>), process creation watched via Win32_Process
with parent resolution. Payloads write a marker file and then sleep, so nothing
is lost to sampling.

// A - string form
{ "type": "command", "command": "C:/.../launcher.exe -CommandBase64 <payload>" }

// B - command + args
{ "type": "command", "command": "C:/.../launcher.exe",
  "args": ["-CommandBase64", "<payload>"] }

Both fired. Result:

| form | processes created | parent of the exe |
|---|---|---|
| A, string | bash.exe and launcher.exe | wrapper present |
| B, args | launcher.exe only | claude.exe, directly |

@marcodelpin's premise holds. A hook command written as a string is wrapped
in bash -c even when argv[0] is a plain .exe with no shell metacharacters.
I had read a difference between two sets of hooks on my own machine as evidence
that the runtime inspects argv[0]. It does not. The difference was the args
field, and I was wrong about the cause.

What is new: args is an existing bypass. Declaring command plus an
args array makes the runtime spawn the executable directly, with no shell in
the chain. That works today, with no runtime change, and it is why hooks
rewritten into that form have no bash parent. Any plugin whose hook command is
a fixed executable can convert to it now.

It also means the direct-spawn machinery already exists. The distance between
that and @marcodelpin's proposal is only the derivation step: deriving args
automatically when a string command has no shell metacharacters would route
those commands through a path that already works.

Scope note so this is not read as more than it is. This measures spawn shape,
not which spawn allocates the visible window. On this machine the bash wrapper
itself has never produced one, so my flashes came from further down the chain.
That may differ from the environment in @marcodelpin's measurements, and the
two questions are worth keeping separate.

NiceLeader · 13 days ago

A scoping correction to my Aug 10/12 comments, after trying to reproduce the
flash deliberately on this machine and failing in every configuration.

I ran the grandchild matrix from a hidden parent console: bash -> node
(2.5 s sleep), bash -> node -> spawnSync(bun, {windowsHide:true}),
bash -> node -> spawnSync(bun) with no flag at all, and
spawn(bun, {detached:true, stdio:'ignore'}) mirroring a daemon respawn.
Window visibility polled via IsWindowVisible, not process creation. Zero
visible windows in every cell, including the deliberately-unhidden ones.

Then I checked the one machine property @lnorton89 flagged:
HKCU:\Console\%%Startup here is {B23D10C0-E52E-411E-9D5B-C09FDF709C7D} -
classic conhost delegation, not Windows Terminal. Under conhost delegation a
child of a hidden console inherits that console, so an unhidden grandchild has
nothing to pop.

Two consequences:

  1. My earlier "the bash wrapper is hidden and never flashes here" observation

is delegation-scoped evidence, not a general result. On WT-delegation
machines ({2EACA947-...} / {E12CFF52-...}) the same spawn shapes may
behave differently, and my machine cannot arbitrate which layer of the
chain allocates the visible window there.

  1. Anyone posting "cannot reproduce" (me included, apparently) should attach

that registry value; a conhost-delegation machine may be structurally
incapable of the dramatic per-call WindowsTerminal.exe symptom. One-liner:
Get-ItemProperty "HKCU:\Console\%%Startup" | Select Delegation*

For the record, one theory I tested and refuted rather than posted: a
detached:true, stdio:'ignore' daemon respawn from a hidden parent does NOT
create a window here - libuv passes DETACHED_PROCESS on Windows, so the child
gets no console at all, despite the Node docs' "own console window" phrasing.

shangchuanqiytu-ui · 10 days ago

Confirming this reproduces on a vanilla install before any hooks or plugins are involved: with just Claude Code + Git Bash out of the box, every Bash tool call flashes its own console. The details below add a data point on how a hooks-heavy setup multiplies the frequency on top of that baseline.

Environment

  • Windows 11 Enterprise LTSC 2024 (10.0.26100)
  • Claude Code v2.1.226, native terminal (no WSL/Dev Container)
  • Git Bash shell backend
  • Plus oh-my-claudecode installed: PreToolUse / PostToolUse / PostToolUseFailure / SessionStart / Stop hooks and a statusline command, all spawning node.exe

Observed

Baseline (vanilla Claude Code, no hooks/plugins):

  • Every shell tool call spawns bash.exe with a visible console flash. Command output and exit status are unaffected — purely visual.

With the hooks-heavy setup layered on top:

  • Each hook event additionally spawns node.exe through a cmd.exe wrapper, which allocates its own console. PreToolUse/PostToolUse match all tools, so a single tool call stacks 2–4 distinct flashes on top of the baseline (bash.exe + hook spawns); a typical session (~40 tool calls) racks up 80+ flashes.
  • The statusline command is the most frequent offender since it renders on every UI update — near-constant flashing while typing.
  • Other spawn paths flash on their own schedules: shell snapshot at session start, IDE-detection polling, MCP stdio servers.

Tried

  • Routing work through the PowerShell tool path doesn't help — the Git Bash snapshot/backend still flashes, and hook events fire for it too since they match all tools.
  • No settings.json option appears to control window suppression for spawned children.

Given what #27115 found (flashing persists even where windowsHide: true is set on the spawn call, pointing at the cmd.exe wrapper layer), it looks like each spawn path needs CREATE_NO_WINDOW applied consistently — or a single global windowsHide-style setting covering all child spawns (shell tools, hooks, statusline, snapshots, MCP stdio) would resolve this whole family at once.

Happy to test any experimental build or flag on this machine.