Files
oh-my-claudecode/scripts/find-node.sh
Yeachan-Heo 800890e691 fix(hooks): resolve node binary for nvm/fnm users (closes #892)
Node.js hooks fail silently for nvm/fnm users because claude Code invokes
hook commands in a non-interactive shell that does not source ~/.bashrc or
~/.zshrc, so bare `node` is not on PATH.

Changes:
- Add src/utils/resolve-node.ts with resolveNodeBinary() that probes
  process.execPath → which node → nvm paths → fnm paths → system paths
  and returns an absolute path (never bare 'node' when avoidable).
- Add scripts/find-node.sh, a POSIX shell wrapper that reads the stored
  nodeBinary from ~/.claude/.omc-config.json then falls back through the
  same priority chain; exits 0 on failure so hooks never block Claude Code.
- Update hooks/hooks.json to invoke all hook scripts via find-node.sh
  instead of bare `node`, using sh "${CLAUDE_PLUGIN_ROOT}/scripts/find-node.sh".
- Update installer/index.ts to use resolveNodeBinary() for the HUD
  statusLine command and to persist the detected path as nodeBinary in
  ~/.claude/.omc-config.json for use by find-node.sh at runtime.
- Update scripts/plugin-setup.mjs to use process.execPath for the HUD
  statusLine and to persist nodeBinary to .omc-config.json.
- Add nodeBinary?: string field to OMCConfig interface in auto-update.ts.
- Add src/__tests__/resolve-node.test.ts with 10 tests covering
  pickLatestVersion() and resolveNodeBinary() (all passing).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 02:52:21 +00:00

91 lines
3.4 KiB
Bash
Executable File

#!/bin/sh
# OMC Node.js Finder (find-node.sh)
#
# Locates the Node.js binary and executes it with the provided arguments.
# Designed for nvm/fnm users where `node` is not on PATH in non-interactive
# shells (e.g. Claude Code hook invocations). Fixes issue #892.
#
# Priority:
# 1. nodeBinary stored in ~/.claude/.omc-config.json (set at setup time)
# 2. `which node` (node is on PATH)
# 3. nvm versioned paths (~/.nvm/versions/node/*/bin/node)
# 4. fnm versioned paths (~/.fnm/node-versions/*/installation/bin/node)
# 5. Homebrew / system paths (/opt/homebrew/bin/node, /usr/local/bin/node)
#
# Exits 0 on failure so it never blocks Claude Code hook processing.
NODE_BIN=""
# ---------------------------------------------------------------------------
# 1. Read stored node path from OMC config
# ---------------------------------------------------------------------------
CLAUDE_DIR="${CLAUDE_CONFIG_DIR:-$HOME/.claude}"
CONFIG_FILE="$CLAUDE_DIR/.omc-config.json"
if [ -f "$CONFIG_FILE" ]; then
# POSIX-safe extraction without requiring jq
_stored=$(grep -o '"nodeBinary" *: *"[^"]*"' "$CONFIG_FILE" 2>/dev/null \
| head -1 \
| sed 's/.*"nodeBinary" *: *"//;s/".*//')
if [ -n "$_stored" ] && [ -x "$_stored" ]; then
NODE_BIN="$_stored"
fi
fi
# ---------------------------------------------------------------------------
# 2. which node
# ---------------------------------------------------------------------------
if [ -z "$NODE_BIN" ] && command -v node >/dev/null 2>&1; then
NODE_BIN="node"
fi
# ---------------------------------------------------------------------------
# 3. nvm versioned paths: iterate to find the latest installed version
# ---------------------------------------------------------------------------
if [ -z "$NODE_BIN" ] && [ -d "$HOME/.nvm/versions/node" ]; then
# shellcheck disable=SC2231
for _path in "$HOME/.nvm/versions/node/"*/bin/node; do
[ -x "$_path" ] && NODE_BIN="$_path"
# Keep iterating — later entries tend to be newer (lexicographic order)
done
fi
# ---------------------------------------------------------------------------
# 4. fnm versioned paths (Linux and macOS default locations)
# ---------------------------------------------------------------------------
if [ -z "$NODE_BIN" ]; then
for _fnm_base in \
"$HOME/.fnm/node-versions" \
"$HOME/Library/Application Support/fnm/node-versions" \
"$HOME/.local/share/fnm/node-versions"; do
if [ -d "$_fnm_base" ]; then
# shellcheck disable=SC2231
for _path in "$_fnm_base/"*/installation/bin/node; do
[ -x "$_path" ] && NODE_BIN="$_path"
done
[ -n "$NODE_BIN" ] && break
fi
done
fi
# ---------------------------------------------------------------------------
# 5. Common Homebrew / system paths
# ---------------------------------------------------------------------------
if [ -z "$NODE_BIN" ]; then
for _path in /opt/homebrew/bin/node /usr/local/bin/node /usr/bin/node; do
if [ -x "$_path" ]; then
NODE_BIN="$_path"
break
fi
done
fi
# ---------------------------------------------------------------------------
# Invoke node with all provided arguments
# ---------------------------------------------------------------------------
if [ -z "$NODE_BIN" ]; then
printf '[OMC] Error: Could not find node binary. Run /oh-my-claudecode:omc-setup to fix.\n' >&2
exit 0 # exit 0 so this hook does not block Claude Code
fi
exec "$NODE_BIN" "$@"