2025-12-01 15:42:12 -08:00
|
|
|
: << 'CMDBLOCK'
|
|
|
|
|
@echo off
|
fix: restore polyglot wrapper to fix Windows hook window spawning
Claude Code spawns hook commands with shell:true + windowsHide:true,
but on Windows the execution chain cmd.exe -> bash.exe causes Git
Bash (MSYS2) to allocate its own console window, bypassing the hide
flag. This creates visible terminal windows that steal focus on every
SessionStart event (startup, resume, clear, compact).
The fix:
- Rename session-start.sh to session-start (no extension) so Claude
Code's .sh auto-detection regex doesn't fire and prepend "bash"
- Restore run-hook.cmd polyglot wrapper to control bash invocation
on Windows (tries known Git Bash paths, then PATH, then exits
silently if no bash found)
- On Unix, the polyglot's shell portion runs the script directly
This avoids Claude Code's broken .sh auto-prepend, gives us control
over how bash is invoked on Windows, and gracefully handles missing
bash instead of erroring.
Addresses: #440, #414, #354, #417, #293
Upstream: anthropics/claude-code#14828
2026-02-10 18:34:58 -08:00
|
|
|
REM Cross-platform polyglot wrapper for hook scripts.
|
|
|
|
|
REM On Windows: cmd.exe runs the batch portion, which finds and calls bash.
|
|
|
|
|
REM On Unix: the shell interprets this as a script (: is a no-op in bash).
|
2026-01-22 13:29:41 -08:00
|
|
|
REM
|
fix: restore polyglot wrapper to fix Windows hook window spawning
Claude Code spawns hook commands with shell:true + windowsHide:true,
but on Windows the execution chain cmd.exe -> bash.exe causes Git
Bash (MSYS2) to allocate its own console window, bypassing the hide
flag. This creates visible terminal windows that steal focus on every
SessionStart event (startup, resume, clear, compact).
The fix:
- Rename session-start.sh to session-start (no extension) so Claude
Code's .sh auto-detection regex doesn't fire and prepend "bash"
- Restore run-hook.cmd polyglot wrapper to control bash invocation
on Windows (tries known Git Bash paths, then PATH, then exits
silently if no bash found)
- On Unix, the polyglot's shell portion runs the script directly
This avoids Claude Code's broken .sh auto-prepend, gives us control
over how bash is invoked on Windows, and gracefully handles missing
bash instead of erroring.
Addresses: #440, #414, #354, #417, #293
Upstream: anthropics/claude-code#14828
2026-02-10 18:34:58 -08:00
|
|
|
REM Hook scripts use extensionless filenames (e.g. "session-start" not
|
|
|
|
|
REM "session-start.sh") so Claude Code's Windows auto-detection -- which
|
|
|
|
|
REM prepends "bash" to any command containing .sh -- doesn't interfere.
|
2026-01-22 13:29:41 -08:00
|
|
|
REM
|
2025-12-01 15:42:12 -08:00
|
|
|
REM Usage: run-hook.cmd <script-name> [args...]
|
|
|
|
|
|
2025-12-01 16:18:56 -08:00
|
|
|
if "%~1"=="" (
|
|
|
|
|
echo run-hook.cmd: missing script name >&2
|
|
|
|
|
exit /b 1
|
|
|
|
|
)
|
fix: restore polyglot wrapper to fix Windows hook window spawning
Claude Code spawns hook commands with shell:true + windowsHide:true,
but on Windows the execution chain cmd.exe -> bash.exe causes Git
Bash (MSYS2) to allocate its own console window, bypassing the hide
flag. This creates visible terminal windows that steal focus on every
SessionStart event (startup, resume, clear, compact).
The fix:
- Rename session-start.sh to session-start (no extension) so Claude
Code's .sh auto-detection regex doesn't fire and prepend "bash"
- Restore run-hook.cmd polyglot wrapper to control bash invocation
on Windows (tries known Git Bash paths, then PATH, then exits
silently if no bash found)
- On Unix, the polyglot's shell portion runs the script directly
This avoids Claude Code's broken .sh auto-prepend, gives us control
over how bash is invoked on Windows, and gracefully handles missing
bash instead of erroring.
Addresses: #440, #414, #354, #417, #293
Upstream: anthropics/claude-code#14828
2026-02-10 18:34:58 -08:00
|
|
|
|
|
|
|
|
set "HOOK_DIR=%~dp0"
|
|
|
|
|
|
|
|
|
|
REM Try Git for Windows bash in standard locations
|
|
|
|
|
if exist "C:\Program Files\Git\bin\bash.exe" (
|
|
|
|
|
"C:\Program Files\Git\bin\bash.exe" "%HOOK_DIR%%~1" %2 %3 %4 %5 %6 %7 %8 %9
|
|
|
|
|
exit /b %ERRORLEVEL%
|
|
|
|
|
)
|
|
|
|
|
if exist "C:\Program Files (x86)\Git\bin\bash.exe" (
|
|
|
|
|
"C:\Program Files (x86)\Git\bin\bash.exe" "%HOOK_DIR%%~1" %2 %3 %4 %5 %6 %7 %8 %9
|
|
|
|
|
exit /b %ERRORLEVEL%
|
|
|
|
|
)
|
Probe per-user Git Bash and Scoop before falling back to PATH on Windows
Stock Windows 10/11 ships C:\Windows\System32\bash.exe (the WSL
launcher) as the first match for `where bash`. WSL's bash cannot
execute Windows-style script paths, so when Git Bash is installed
outside the two standard system locations -- specifically the
per-user "Only for me" Git for Windows installer
(%LOCALAPPDATA%\Programs\Git) or a Scoop install
(%USERPROFILE%\scoop\apps\git\current\usr\bin) -- run-hook.cmd
silently fails: WSL prints "Windows Subsystem for Linux must be
updated", the script returns 0, and Superpowers' SessionStart
bootstrap is never injected. From the user's perspective skills
auto-trigger inconsistently or not at all, with no surfaced error.
Add explicit probes for both locations between the existing system-
wide Git for Windows checks and the `where bash` fallback. Also add
a comment to the fallback documenting the WSL-launcher trap so future
maintainers understand why the explicit probes must come first.
Verified on a Windows 11 VM (dockur/windows 11, Git Bash 2.x, Node
22):
- System Git present: existing probe still matches (no regression)
- System Git absent, per-user Git present via junction: new probe
matches, hook produces valid 6422-byte JSON, exit 0
- All Git probes absent: confirmed WSL trap fires
("Windows Subsystem for Linux must be updated") and the hook exits 0
silently, demonstrating the original bug
Existing tests/hooks/test-session-start.sh still passes on macOS (7/7).
Reported by @ytchenak in #1607.
Co-authored-by: ytchenak <ytchenak@users.noreply.github.com>
Closes #1607.
2026-05-23 16:58:56 -07:00
|
|
|
REM Per-user Git for Windows installer ("Only for me" / winget user scope)
|
|
|
|
|
if exist "%LOCALAPPDATA%\Programs\Git\bin\bash.exe" (
|
|
|
|
|
"%LOCALAPPDATA%\Programs\Git\bin\bash.exe" "%HOOK_DIR%%~1" %2 %3 %4 %5 %6 %7 %8 %9
|
|
|
|
|
exit /b %ERRORLEVEL%
|
|
|
|
|
)
|
|
|
|
|
REM Scoop user install (`scoop install git`)
|
|
|
|
|
if exist "%USERPROFILE%\scoop\apps\git\current\usr\bin\bash.exe" (
|
|
|
|
|
"%USERPROFILE%\scoop\apps\git\current\usr\bin\bash.exe" "%HOOK_DIR%%~1" %2 %3 %4 %5 %6 %7 %8 %9
|
|
|
|
|
exit /b %ERRORLEVEL%
|
|
|
|
|
)
|
fix: restore polyglot wrapper to fix Windows hook window spawning
Claude Code spawns hook commands with shell:true + windowsHide:true,
but on Windows the execution chain cmd.exe -> bash.exe causes Git
Bash (MSYS2) to allocate its own console window, bypassing the hide
flag. This creates visible terminal windows that steal focus on every
SessionStart event (startup, resume, clear, compact).
The fix:
- Rename session-start.sh to session-start (no extension) so Claude
Code's .sh auto-detection regex doesn't fire and prepend "bash"
- Restore run-hook.cmd polyglot wrapper to control bash invocation
on Windows (tries known Git Bash paths, then PATH, then exits
silently if no bash found)
- On Unix, the polyglot's shell portion runs the script directly
This avoids Claude Code's broken .sh auto-prepend, gives us control
over how bash is invoked on Windows, and gracefully handles missing
bash instead of erroring.
Addresses: #440, #414, #354, #417, #293
Upstream: anthropics/claude-code#14828
2026-02-10 18:34:58 -08:00
|
|
|
|
Probe per-user Git Bash and Scoop before falling back to PATH on Windows
Stock Windows 10/11 ships C:\Windows\System32\bash.exe (the WSL
launcher) as the first match for `where bash`. WSL's bash cannot
execute Windows-style script paths, so when Git Bash is installed
outside the two standard system locations -- specifically the
per-user "Only for me" Git for Windows installer
(%LOCALAPPDATA%\Programs\Git) or a Scoop install
(%USERPROFILE%\scoop\apps\git\current\usr\bin) -- run-hook.cmd
silently fails: WSL prints "Windows Subsystem for Linux must be
updated", the script returns 0, and Superpowers' SessionStart
bootstrap is never injected. From the user's perspective skills
auto-trigger inconsistently or not at all, with no surfaced error.
Add explicit probes for both locations between the existing system-
wide Git for Windows checks and the `where bash` fallback. Also add
a comment to the fallback documenting the WSL-launcher trap so future
maintainers understand why the explicit probes must come first.
Verified on a Windows 11 VM (dockur/windows 11, Git Bash 2.x, Node
22):
- System Git present: existing probe still matches (no regression)
- System Git absent, per-user Git present via junction: new probe
matches, hook produces valid 6422-byte JSON, exit 0
- All Git probes absent: confirmed WSL trap fires
("Windows Subsystem for Linux must be updated") and the hook exits 0
silently, demonstrating the original bug
Existing tests/hooks/test-session-start.sh still passes on macOS (7/7).
Reported by @ytchenak in #1607.
Co-authored-by: ytchenak <ytchenak@users.noreply.github.com>
Closes #1607.
2026-05-23 16:58:56 -07:00
|
|
|
REM Try bash on PATH (e.g. user-installed Git Bash, MSYS2, Cygwin). Note that
|
|
|
|
|
REM on stock Windows 10/11 `where bash` resolves to C:\Windows\System32\bash.exe
|
|
|
|
|
REM (the WSL launcher), which fails on Windows-style script paths. The explicit
|
|
|
|
|
REM probes above must therefore be exhausted first.
|
fix: restore polyglot wrapper to fix Windows hook window spawning
Claude Code spawns hook commands with shell:true + windowsHide:true,
but on Windows the execution chain cmd.exe -> bash.exe causes Git
Bash (MSYS2) to allocate its own console window, bypassing the hide
flag. This creates visible terminal windows that steal focus on every
SessionStart event (startup, resume, clear, compact).
The fix:
- Rename session-start.sh to session-start (no extension) so Claude
Code's .sh auto-detection regex doesn't fire and prepend "bash"
- Restore run-hook.cmd polyglot wrapper to control bash invocation
on Windows (tries known Git Bash paths, then PATH, then exits
silently if no bash found)
- On Unix, the polyglot's shell portion runs the script directly
This avoids Claude Code's broken .sh auto-prepend, gives us control
over how bash is invoked on Windows, and gracefully handles missing
bash instead of erroring.
Addresses: #440, #414, #354, #417, #293
Upstream: anthropics/claude-code#14828
2026-02-10 18:34:58 -08:00
|
|
|
where bash >nul 2>nul
|
|
|
|
|
if %ERRORLEVEL% equ 0 (
|
|
|
|
|
bash "%HOOK_DIR%%~1" %2 %3 %4 %5 %6 %7 %8 %9
|
|
|
|
|
exit /b %ERRORLEVEL%
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
REM No bash found - exit silently rather than error
|
|
|
|
|
REM (plugin still works, just without SessionStart context injection)
|
|
|
|
|
exit /b 0
|
2025-12-01 15:42:12 -08:00
|
|
|
CMDBLOCK
|
|
|
|
|
|
fix: restore polyglot wrapper to fix Windows hook window spawning
Claude Code spawns hook commands with shell:true + windowsHide:true,
but on Windows the execution chain cmd.exe -> bash.exe causes Git
Bash (MSYS2) to allocate its own console window, bypassing the hide
flag. This creates visible terminal windows that steal focus on every
SessionStart event (startup, resume, clear, compact).
The fix:
- Rename session-start.sh to session-start (no extension) so Claude
Code's .sh auto-detection regex doesn't fire and prepend "bash"
- Restore run-hook.cmd polyglot wrapper to control bash invocation
on Windows (tries known Git Bash paths, then PATH, then exits
silently if no bash found)
- On Unix, the polyglot's shell portion runs the script directly
This avoids Claude Code's broken .sh auto-prepend, gives us control
over how bash is invoked on Windows, and gracefully handles missing
bash instead of erroring.
Addresses: #440, #414, #354, #417, #293
Upstream: anthropics/claude-code#14828
2026-02-10 18:34:58 -08:00
|
|
|
# Unix: run the named script directly
|
2026-02-21 10:40:30 -08:00
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
2025-12-01 15:42:12 -08:00
|
|
|
SCRIPT_NAME="$1"
|
|
|
|
|
shift
|
fix: restore polyglot wrapper to fix Windows hook window spawning
Claude Code spawns hook commands with shell:true + windowsHide:true,
but on Windows the execution chain cmd.exe -> bash.exe causes Git
Bash (MSYS2) to allocate its own console window, bypassing the hide
flag. This creates visible terminal windows that steal focus on every
SessionStart event (startup, resume, clear, compact).
The fix:
- Rename session-start.sh to session-start (no extension) so Claude
Code's .sh auto-detection regex doesn't fire and prepend "bash"
- Restore run-hook.cmd polyglot wrapper to control bash invocation
on Windows (tries known Git Bash paths, then PATH, then exits
silently if no bash found)
- On Unix, the polyglot's shell portion runs the script directly
This avoids Claude Code's broken .sh auto-prepend, gives us control
over how bash is invoked on Windows, and gracefully handles missing
bash instead of erroring.
Addresses: #440, #414, #354, #417, #293
Upstream: anthropics/claude-code#14828
2026-02-10 18:34:58 -08:00
|
|
|
exec bash "${SCRIPT_DIR}/${SCRIPT_NAME}" "$@"
|