SessionStart hooks (run-hook.cmd, session-start) fail when PATH is broken: dirname/cat/bash not found
Summary
The SessionStart hook scripts (hooks/run-hook.cmd and hooks/session-start) call external commands (dirname, cat, and re-invoke bash via PATH lookup) that are not guaranteed to be resolvable in the environment Claude Code spawns hooks in. On a system where PATH is corrupted/empty at hook-spawn time (a known Claude Code issue — anthropics/claude-code#43127 and related), these hooks fail outright:
SessionStart:startup hook error
Failed with non-blocking status code: .../hooks/run-hook.cmd: line 43: dirname: command not found
and, after working around that one, the next external dependency surfaces:
SessionStart:startup hook error
Failed with non-blocking status code: .../hooks/session-start: line 43: cat: command not found
Since this is a SessionStart:startup hook — one of the very first things that runs — it appears to race ahead of any user-side PATH-repair SessionStart hooks (they run in the same startup batch, so a fix that backfills PATH for future subprocesses doesn't help a sibling hook in the same batch). So plugin hooks that rely on PATH at all are exposed to this a lot more than regular tool-invoked hooks.
Fix
None of these calls need an external binary — bash builtins cover all of it:
hooks/run-hook.cmd,hooks/session-start:SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"→SCRIPT_DIR="$(cd "${0%/*}" && pwd)"hooks/run-hook.cmd:exec bash "${SCRIPT_DIR}/${SCRIPT_NAME}" "$@"→exec "${BASH:-bash}" "${SCRIPT_DIR}/${SCRIPT_NAME}" "$@"(bash sets$BASHto its own absolute path, noPATHlookup needed)hooks/session-start:cat "${PLUGIN_ROOT}/skills/using-superpowers/SKILL.md"→$(<"${PLUGIN_ROOT}/skills/using-superpowers/SKILL.md")(builtin redirection read), guarded with[ -r ... ]instead of... || echo "Error reading..."hooks/session-start: the trailing| caton each of the threeprintfbranches is a no-op pass-through and can just be removed
I patched a local copy (plugin cache, v6.3.0) this way and verified both a normal run and a run under a completely empty PATH (env -i PATH= ... bash run-hook.cmd session-start) produce identical, correct JSON output.
Environment
- Claude Code v2.1.274
- superpowers plugin v6.3.0 (official marketplace)
- Fedora Linux, zsh
Suggested patch
--- a/hooks/run-hook.cmd
+++ b/hooks/run-hook.cmd
@@
# Unix: run the named script directly
-SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+SCRIPT_DIR="$(cd "${0%/*}" && pwd)"
SCRIPT_NAME="$1"
shift
-exec bash "${SCRIPT_DIR}/${SCRIPT_NAME}" "$@"
+exec "${BASH:-bash}" "${SCRIPT_DIR}/${SCRIPT_NAME}" "$@"
--- a/hooks/session-start
+++ b/hooks/session-start
@@
# Determine plugin root directory
-SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+SCRIPT_DIR="$(cd "${0%/*}" && pwd)"
PLUGIN_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
# Read using-superpowers content
-using_superpowers_content=$(cat "${PLUGIN_ROOT}/skills/using-superpowers/SKILL.md" 2>&1 || echo "Error reading using-superpowers skill")
+if [ -r "${PLUGIN_ROOT}/skills/using-superpowers/SKILL.md" ]; then
+ using_superpowers_content=$(<"${PLUGIN_ROOT}/skills/using-superpowers/SKILL.md")
+else
+ using_superpowers_content="Error reading using-superpowers skill"
+fi
@@
if [ -n "${CURSOR_PLUGIN_ROOT:-}" ]; then
# Cursor sets CURSOR_PLUGIN_ROOT (may also set CLAUDE_PLUGIN_ROOT)
- printf '{\n "additional_context": "%s"\n}\n' "$session_context" | cat
+ printf '{\n "additional_context": "%s"\n}\n' "$session_context"
elif [ -n "${CLAUDE_PLUGIN_ROOT:-}" ] && [ -z "${COPILOT_CLI:-}" ]; then
# Claude Code sets CLAUDE_PLUGIN_ROOT without COPILOT_CLI
- printf '{\n "hookSpecificOutput": {\n "hookEventName": "SessionStart",\n "additionalContext": "%s"\n }\n}\n' "$session_context" | cat
+ printf '{\n "hookSpecificOutput": {\n "hookEventName": "SessionStart",\n "additionalContext": "%s"\n }\n}\n' "$session_context"
else
# Copilot CLI (sets COPILOT_CLI=1) or unknown platform — SDK standard format
- printf '{\n "additionalContext": "%s"\n}\n' "$session_context" | cat
+ printf '{\n "additionalContext": "%s"\n}\n' "$session_context"
fi
Happy to open a PR with this if useful.
Source: obra/superpowers