fix(design-gate): resolve claude on a sanitized PATH (real cause of unknown)
The "unknown -> exit 11" path triggers when `command -v claude` fails. Root cause is NOT the interactive alias (claude->dtach_claude) not surviving the subshell — that's true but harmless: the real binary is on the inherited PATH, so `command -v` finds it in a normal `bash script.sh` (proven: toggle-external and the gate both resolve claude). The actual lever is PATH carrying the nvm node bin. A skill/hook that shells the gate out with a sanitized PATH, or a node upgrade moving the version-pinned nvm path, loses it. ensure_claude_on_path(): if `command -v claude` already resolves, do nothing; else probe known install dirs (~/.claude/local, ~/.local/bin, /usr/local/bin) and the nvm glob, prepending the bin dir — which carries BOTH claude and its node runtime (claude's shebang needs node, same dir). nvm keeps old versions after an upgrade, so pick the newest that ships claude via sort -V, not the first glob match. If nothing resolves, command -v still fails -> unknown -> exit 11 (fail-visible net stays). Verified: shellcheck clean; normal PATH -> READY exit 0 (function returns early, no regression); PATH=/usr/bin:/bin (sanitized hook) -> now resolves claude via the nvm glob and reports REAL magic state (READY exit 0), where before the fix it was exit 11 unknown. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
60031c1bef
commit
f96331844c
@ -51,6 +51,33 @@ PROFILE_FILE="$PROFILES_DIR/$PROFILE.profile"
|
||||
[ -x "$PROFILE_SH" ] || { echo "design-gate: profile.sh not executable at $PROFILE_SH" >&2; exit 2; }
|
||||
[ -f "$PROFILE_FILE" ] || { echo "design-gate: profile '$PROFILE' not found" >&2; exit 2; }
|
||||
|
||||
# Ensure the claude CLI + its node runtime are reachable even when a skill/hook
|
||||
# shells this script out with a sanitized PATH. The interactive alias
|
||||
# claude->dtach_claude never reaches a non-interactive subshell; the real binary
|
||||
# AND its node bin dir are what matter (claude's shebang needs node, same dir).
|
||||
# If `command -v claude` already resolves, do nothing; else probe known install
|
||||
# dirs and prepend. nvm keeps old node versions after an upgrade, so pick the
|
||||
# newest that actually ships claude (sort -V), not the first glob match.
|
||||
ensure_claude_on_path() {
|
||||
command -v claude >/dev/null 2>&1 && return
|
||||
local cand
|
||||
for cand in \
|
||||
"$HOME/.claude/local/claude" \
|
||||
"$HOME/.local/bin/claude" \
|
||||
/usr/local/bin/claude; do
|
||||
[ -x "$cand" ] && { PATH="$(dirname "$cand"):$PATH"; return; }
|
||||
done
|
||||
local m newest matches=()
|
||||
for m in "$HOME"/.nvm/versions/node/*/bin/claude; do
|
||||
[ -x "$m" ] && matches+=("$m")
|
||||
done
|
||||
if [ "${#matches[@]}" -gt 0 ]; then
|
||||
newest="$(printf '%s\n' "${matches[@]}" | sort -V | tail -1)"
|
||||
PATH="$(dirname "$newest"):$PATH"
|
||||
fi
|
||||
}
|
||||
ensure_claude_on_path
|
||||
|
||||
# Gate scope: the "# GATE-BLOCK:" allowlist (one or more lines, concatenated).
|
||||
# Empty => fall back to "every gate-relevant entry is in scope" (coarse).
|
||||
core_set="$(grep '^# GATE-BLOCK:' "$PROFILE_FILE" 2>/dev/null \
|
||||
|
||||
Loading…
Reference in New Issue
Block a user