Problem
The modern-python/hooks/setup-shims.sh SessionStart hook prepends a python3 shim to PATH that always exit 1s. This breaks every Claude Code settings.json hook that invokes bare python3 /path/to/hook.py: the hook command silently fails because most are wrapped with 2>/dev/null || true (or rely on the harness treating exit 1 as a soft skip), so the user never sees the shim's stderr nudge.
Concrete impact on my setup (~25 user-defined hooks broken since 1.5.0 landed on my system on 2026-05-09):
- Stop hooks (worktree-archive-autoprep, check-fabricated-hook-attribution, check-fabricated-panelist)
- SessionEnd hooks (reconcile-worktree-shared)
- PreToolUse hooks (vault-command-nudges, block-raw-vault-git, several others)
- PostToolUse hooks (hookify-auto-commit, etc.)
The shim's intent is a teaching nudge for human-typed python3 -m pip install. In automated contexts, no human reads the nudge. The cost is silent breakage.
Repro
- Install
trailofbits/modern-python plugin.
- Add any user hook to
~/.claude/settings.json that runs python3 /path/to/script.py.
- Trigger the hook. Script never executes; stderr goes to harness log only.
Suggested fix
Bypass the shim when CLAUDECODE=1 (Claude Code sets this in every subprocess: hooks, Bash tool, agents). The nudge still fires in plain terminal shells where the user typed python3 ... directly without invoking Claude Code.
Verified patch against 1.5.0 hooks/shims/python (top of file, before the case statement):
if [[ "${CLAUDECODE:-}" == "1" ]]; then
shim_dir="${BASH_SOURCE[0]%/*}"
shim_dir="$(cd "$shim_dir" && pwd)"
clean_path=":$PATH:"
clean_path="${clean_path//:$shim_dir:/:}"
clean_path="${clean_path#:}"
clean_path="${clean_path%:}"
PATH="$clean_path" exec "$cmd" "$@"
fi
If preferred, narrower variants:
- Skip only on
python3 /absolute/path/to/script.py invocations (let -m, -c keep nudging).
- Skip when stdin is not a tty.
CLAUDECODE=1 is the cleanest because it precisely targets the harm domain (Claude Code automation) without affecting the nudge's intended audience (humans typing in terminal).
Happy to send a PR.
Environment
- macOS, Claude Code 2.1.128
- modern-python plugin 1.5.0 (installed 2026-05-09 23:27 local)
- Affected hook surfaces: PreToolUse, PostToolUse, Stop, SessionStart, SessionEnd, UserPromptSubmit
Problem
The
modern-python/hooks/setup-shims.shSessionStart hook prepends apython3shim to PATH that alwaysexit 1s. This breaks every Claude Code settings.json hook that invokes barepython3 /path/to/hook.py: the hook command silently fails because most are wrapped with2>/dev/null || true(or rely on the harness treating exit 1 as a soft skip), so the user never sees the shim's stderr nudge.Concrete impact on my setup (~25 user-defined hooks broken since 1.5.0 landed on my system on 2026-05-09):
The shim's intent is a teaching nudge for human-typed
python3 -m pip install. In automated contexts, no human reads the nudge. The cost is silent breakage.Repro
trailofbits/modern-pythonplugin.~/.claude/settings.jsonthat runspython3 /path/to/script.py.Suggested fix
Bypass the shim when
CLAUDECODE=1(Claude Code sets this in every subprocess: hooks, Bash tool, agents). The nudge still fires in plain terminal shells where the user typedpython3 ...directly without invoking Claude Code.Verified patch against 1.5.0
hooks/shims/python(top of file, before the case statement):If preferred, narrower variants:
python3 /absolute/path/to/script.pyinvocations (let-m,-ckeep nudging).CLAUDECODE=1 is the cleanest because it precisely targets the harm domain (Claude Code automation) without affecting the nudge's intended audience (humans typing in terminal).
Happy to send a PR.
Environment