Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions .claude/hooks/session-start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,48 @@ point_pytest_at_venv() {
done
}

# 2c. Every OTHER console script the venv owns.
#
# `pytest` and `python3` survive without the venv on PATH because 2 and 2b give
# each one its own shim. Nothing else does — and PATH is exactly what a
# multi-repo session never gets, because the env file that exports it is written
# by Claude Code around a hook it never registers (see leg 5). So a dependency
# declared in `.claude/session-python.txt` that ships a CONSOLE SCRIPT rather
# than an importable module was installed into the venv and then invisible.
#
# Measured, not hypothetical: PyAutoHands shells out to `ipynb-py-convert`, and
# with the package installed its suite still failed five tests with
# `FileNotFoundError: ipynb-py-convert` — the binary sitting in $VENV/bin,
# unreachable, while `--check` called the session healthy. A missing binary and
# an unreachable one are the same symptom, and neither says "environment".
#
# The claim policy is 2b's, because the reasoning is 2b's: a name we can reach
# only through uv's shim dir or not at all is ours to provide; a name the image
# owns elsewhere is not ours to overwrite.
point_venv_scripts_at_venv() {
local shim_dir="${HOME}/.local/bin" entry name path
[ -d "$VENV/bin" ] || return 0
mkdir -p "$shim_dir" 2>/dev/null || return 0
for entry in "$VENV"/bin/*; do
[ -f "$entry" ] && [ -x "$entry" ] || continue
name="$(basename "$entry")"
case "$name" in
# python/pip are legs 2 and the venv's own plumbing; pytest is 2b.
# The activate family is meant to be sourced, never executed.
python*|pip*|pytest|py.test|activate*|deactivate*|*.bat|*.ps1|*.csh|*.fish|*.nu) continue ;;
esac
path="$(command -v "$name" 2>/dev/null)" || path=""
case "$path" in
"$VENV"/*) continue ;;
"") ;;
"$shim_dir"/*) ;;
*) log "WARNING: $name resolves to $path, outside uv's shim dir — left alone"; continue ;;
esac
write_venv_shim "$shim_dir/$name" "$entry" \
&& log "$shim_dir/$name -> $entry (venv console script)"
done
}

# 3. The uv-managed tools — rebuilt on 3.12 only where they are not already.
#
# Pinned to the version already installed: this hook changes the INTERPRETER a
Expand Down Expand Up @@ -434,6 +476,7 @@ if ensure_venv; then
point_system_default
retool_uv_tools
point_pytest_at_venv
point_venv_scripts_at_venv
# Every repo in the session registers this hook, so the second copy must not
# prepend the venv a second time.
if [ -n "${CLAUDE_ENV_FILE:-}" ] && ! grep -qs 'PYAUTO_SESSION_PY312=' "$CLAUDE_ENV_FILE"; then
Expand Down
Loading