Procedure
症状1: hook が /bin/sh: node: command not found(また brew/その他CLIも)
GUIから起動したClaude Code(.app)が spawn する hook の /bin/sh -c は、ログインシェルの
プロファイル(.zshrc)を読まず 最小PATH(/usr/bin:/bin:/usr/sbin:/sbin) しか持たない。
nvm/homebrew にある node 等が見えず、プラグイン定義の node "...mjs" 系hookが毎回失敗する。
修正: ~/.claude/settings.json のトップレベルに env.PATH を足し、hook子プロセスに継承させる。
"env": {
"PATH": "/Users/<you>/.nvm/versions/node/<ver>/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/<you>/.local/bin"
}
Bashツール側はプロファイルを再読込するので pyenv/nvm の順序は壊れない(env.PATHは hook など
プロファイル非読込の子にだけ効く)。nvm versioned path が将来stale化しても /opt/homebrew/bin/node
が fallback するので壊れない。
症状2: スクリプトの timeout 60 ... が効かない(timeout: command not found)
macOSは GNU coreutils の timeout を標準搭載しない。ECCのhooks.md等が前提にしていても不発。
修正: brew install coreutils で gtimeout が入る。スクリプトは
command -v gtimeout >/dev/null && TIMEOUT_CMD="gtimeout 60" の形で優先検出させる
(post_tsc_check.sh / skills-auto-update.sh はこの形。coreutils導入だけで設計通りに起動する)。
Pitfalls
env.PATHを短すぎる値にするとin-sessionのBashが影響を受けうるので、ログインPATHの主要dirを網羅した superset にする。設定前にcp settings.json backups/でバックアップ。settings.jsonはプラグイン(例: agentmemory/remember)がライブ書換することがある。Edit前に再Readして楽観ロックに従う。envブロックはjq round-tripでも保全される。- nvm の versioned path をハードコードすると node メジャー更新でstaleになる → 必ず
/opt/homebrew/binも含める。
Verification
# 旧最小PATHで再現(not found が出る)
env -i HOME="$HOME" /bin/sh -c 'PATH="/usr/bin:/bin"; node --version'
# 新env.PATHで解決(バージョンが出る)
env -i HOME="$HOME" /bin/sh -c 'PATH="<settings.jsonのenv.PATH>"; node --version && command -v node'
# timeout
command -v gtimeout && echo "post_tsc_check 等の timeout 分岐が起動する"
jq -e . ~/.claude/settings.json >/dev/null && echo "settings.json VALID"