Problem
On Windows, every hook in hooks/hooks.json opens a visible Git Bash console window on session start (and one more on session end). With three SessionStart hooks, three black bash windows pop up briefly every time Claude Code starts.
Window title:
/usr/bin/bash --login -i -c node "/c/Users/.../plugins/cache/.../hooks/session-start-seen-skills.mjs"
The -i (interactive) flag is what forces Git Bash to show a real terminal window.
Reproduction
- Windows 11, Git for Windows installed, Claude Code installed
- Install
vercel-plugin@0.42.1
- Open Claude Code
- Three black
bash --login -i -c node "..." windows flash on startup, one per SessionStart hook
Root cause
Claude Code''s Windows harness wraps every hook command string in bash --login -i -c "<cmd>" when no shell field is set on the hook.
Anthropic closed both upstream feature requests for hiding the console window:
So no fix is coming from the harness side; it has to be configured per-hook by plugins.
Suggested fix
Per the official hooks reference, each command hook accepts a "shell" field with values "bash" (default) or "powershell". Setting "shell": "powershell" makes the harness spawn the command via PowerShell directly, with no bash --login -i wrapping, and PowerShell is spawned hidden on Windows.
Patch for hooks/hooks.json:
{
"hooks": {
"SessionStart": [
{
"matcher": "startup|resume|clear|compact",
"hooks": [
{
"type": "command",
- "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/session-start-seen-skills.mjs\""
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/session-start-seen-skills.mjs\"",
+ "shell": "powershell"
},
{
"type": "command",
- "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/session-start-profiler.mjs\""
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/session-start-profiler.mjs\"",
+ "shell": "powershell"
},
{
"type": "command",
- "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/inject-claude-md.mjs\""
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/inject-claude-md.mjs\"",
+ "shell": "powershell"
}
]
}
],
"SessionEnd": [
{
"hooks": [
{
"type": "command",
- "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/session-end-cleanup.mjs\""
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/session-end-cleanup.mjs\"",
+ "shell": "powershell"
}
]
}
]
}
}
Verified locally on 2026-05-10 with vercel-plugin 0.42.1 — applying the patch eliminates the visible bash windows. The hooks themselves still execute correctly (skill injection, profiler, cleanup all working).
This change is safe on macOS/Linux because "shell": "powershell" is only honored on Windows; on Unix the harness falls back to its default behavior. (Worth confirming with a quick CI run on the existing platform-hook-compat tests.)
Environment
- OS: Windows 11 Home 10.0.26200
- Claude Code: v2.1.x (bundled
claude.exe, ~226MB)
- vercel-plugin: 0.42.1
- Git Bash:
C:\Program Files\Git\bin\bash.exe
- Node: bundled with Claude Code
Problem
On Windows, every hook in
hooks/hooks.jsonopens a visible Git Bash console window on session start (and one more on session end). With threeSessionStarthooks, three black bash windows pop up briefly every time Claude Code starts.Window title:
The
-i(interactive) flag is what forces Git Bash to show a real terminal window.Reproduction
vercel-plugin@0.42.1bash --login -i -c node "..."windows flash on startup, one perSessionStarthookRoot cause
Claude Code''s Windows harness wraps every hook
commandstring inbash --login -i -c "<cmd>"when noshellfield is set on the hook.Anthropic closed both upstream feature requests for hiding the console window:
So no fix is coming from the harness side; it has to be configured per-hook by plugins.
Suggested fix
Per the official hooks reference, each
commandhook accepts a"shell"field with values"bash"(default) or"powershell". Setting"shell": "powershell"makes the harness spawn the command via PowerShell directly, with nobash --login -iwrapping, and PowerShell is spawned hidden on Windows.Patch for
hooks/hooks.json:{ "hooks": { "SessionStart": [ { "matcher": "startup|resume|clear|compact", "hooks": [ { "type": "command", - "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/session-start-seen-skills.mjs\"" + "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/session-start-seen-skills.mjs\"", + "shell": "powershell" }, { "type": "command", - "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/session-start-profiler.mjs\"" + "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/session-start-profiler.mjs\"", + "shell": "powershell" }, { "type": "command", - "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/inject-claude-md.mjs\"" + "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/inject-claude-md.mjs\"", + "shell": "powershell" } ] } ], "SessionEnd": [ { "hooks": [ { "type": "command", - "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/session-end-cleanup.mjs\"" + "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/session-end-cleanup.mjs\"", + "shell": "powershell" } ] } ] } }Verified locally on 2026-05-10 with vercel-plugin 0.42.1 — applying the patch eliminates the visible bash windows. The hooks themselves still execute correctly (skill injection, profiler, cleanup all working).
This change is safe on macOS/Linux because
"shell": "powershell"is only honored on Windows; on Unix the harness falls back to its default behavior. (Worth confirming with a quick CI run on the existingplatform-hook-compattests.)Environment
claude.exe, ~226MB)C:\Program Files\Git\bin\bash.exe