mirror of
https://github.com/obra/superpowers.git
synced 2026-08-06 22:28:47 +08:00
fix(hooks): replace run-hook.cmd heredoc with :; label lines (#571)
bash >= 5.1 writes heredocs into a pipe pre-fork; under macOS pipe pressure the kernel hands out 512-byte pipes and the wrapper's 1.4KB CMDBLOCK heredoc deadlocks every hook. Label lines parse nowhere: cmd.exe skips them as labels, POSIX shells exec away before the batch block. Also: silent fail-open when bash or the script name is missing, and CDPATH-proof directory resolution.
This commit is contained in:
@@ -1,8 +1,20 @@
|
|||||||
: << 'CMDBLOCK'
|
:; command -v bash >/dev/null 2>&1 || exit 0
|
||||||
|
:; [ $# -ge 1 ] || exit 0
|
||||||
|
:; SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
|
||||||
|
:; SCRIPT_NAME="$1"; shift; exec bash "${SCRIPT_DIR}/${SCRIPT_NAME}" "$@"
|
||||||
@echo off
|
@echo off
|
||||||
REM Cross-platform polyglot wrapper for hook scripts.
|
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: POSIX shells execute the ":;" lines above and exec away before
|
||||||
REM On Unix: the shell interprets this as a script (: is a no-op in bash).
|
REM reaching this batch block (":" is a no-op; cmd.exe treats lines starting
|
||||||
|
REM with ":" as labels and skips them). If bash or the script name is
|
||||||
|
REM missing, the wrapper exits 0 silently - hooks are optional context, never
|
||||||
|
REM a session breaker.
|
||||||
|
REM On Windows: cmd.exe runs this batch portion, which finds and calls bash.
|
||||||
|
REM
|
||||||
|
REM Heredocs are banned in this file and every hooks/ executable: bash 5.1+
|
||||||
|
REM delivers them via a pre-fork pipe write that deadlocks on macOS under
|
||||||
|
REM pipe pressure (issue #571). tests/hooks/test-no-heredocs-in-hooks.sh is
|
||||||
|
REM the fence.
|
||||||
REM
|
REM
|
||||||
REM Hook scripts use extensionless filenames (e.g. "session-start" not
|
REM Hook scripts use extensionless filenames (e.g. "session-start" not
|
||||||
REM "session-start.sh") so Claude Code's Windows auto-detection -- which
|
REM "session-start.sh") so Claude Code's Windows auto-detection -- which
|
||||||
@@ -35,12 +47,4 @@ if %ERRORLEVEL% equ 0 (
|
|||||||
)
|
)
|
||||||
|
|
||||||
REM No bash found - exit silently rather than error
|
REM No bash found - exit silently rather than error
|
||||||
REM (plugin still works, just without SessionStart context injection)
|
|
||||||
exit /b 0
|
exit /b 0
|
||||||
CMDBLOCK
|
|
||||||
|
|
||||||
# Unix: run the named script directly
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
||||||
SCRIPT_NAME="$1"
|
|
||||||
shift
|
|
||||||
exec bash "${SCRIPT_DIR}/${SCRIPT_NAME}" "$@"
|
|
||||||
|
|||||||
@@ -184,6 +184,15 @@ assert_command_output \
|
|||||||
CLAUDE_PLUGIN_ROOT="$REPO_ROOT" \
|
CLAUDE_PLUGIN_ROOT="$REPO_ROOT" \
|
||||||
bash "$WRAPPER_UNDER_TEST" session-start
|
bash "$WRAPPER_UNDER_TEST" session-start
|
||||||
|
|
||||||
|
# With no bash available, the wrapper must fail open: silent, exit 0.
|
||||||
|
if output="$(env -i PATH=/nonexistent /bin/sh "$WRAPPER_UNDER_TEST" session-start 2>&1)" \
|
||||||
|
&& [ -z "$output" ]; then
|
||||||
|
pass "wrapper with no bash on PATH is silent and exits 0"
|
||||||
|
else
|
||||||
|
fail "wrapper with no bash on PATH is silent and exits 0"
|
||||||
|
printf '%s\n' "$output" | head -3 | sed 's/^/ /'
|
||||||
|
fi
|
||||||
|
|
||||||
cursor_home="$(make_home cursor)"
|
cursor_home="$(make_home cursor)"
|
||||||
assert_command_output \
|
assert_command_output \
|
||||||
"Cursor emits top-level additional_context only" \
|
"Cursor emits top-level additional_context only" \
|
||||||
|
|||||||
Reference in New Issue
Block a user