From db06ff84e04064dd987cf2268218032d649af6b4 Mon Sep 17 00:00:00 2001 From: A <258483684+la14-1@users.noreply.github.com> Date: Sun, 15 Feb 2026 23:34:09 -0800 Subject: [PATCH] fix: run claude install --force and persist fnm PATH to shell configs (#1245) After installing Claude Code (via any method), run `claude install --force` to set up shell integration, then ensure fnm bootstrap is persisted to both .bashrc and .zshrc so interactive sessions can find node. Also simplify all launch commands across 9 clouds: instead of hardcoding PATH entries that may miss fnm, source the rc files which now contain all the necessary PATH entries from both inject_env_vars and _finalize_claude_install. Co-authored-by: lab <6723574+louisgv@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) --- aws-lightsail/claude.sh | 2 +- daytona/claude.sh | 2 +- digitalocean/claude.sh | 2 +- fly/claude.sh | 2 +- gcp/claude.sh | 2 +- hetzner/claude.sh | 2 +- oracle/claude.sh | 2 +- ovh/claude.sh | 2 +- shared/common.sh | 14 ++++++++++++++ sprite/claude.sh | 4 ++-- 10 files changed, 24 insertions(+), 10 deletions(-) diff --git a/aws-lightsail/claude.sh b/aws-lightsail/claude.sh index 35cdc209..22cea037 100644 --- a/aws-lightsail/claude.sh +++ b/aws-lightsail/claude.sh @@ -69,4 +69,4 @@ echo "" log_step "Starting Claude Code..." sleep 1 clear -interactive_session "${LIGHTSAIL_SERVER_IP}" "export PATH=\$HOME/.local/bin:\$HOME/.bun/bin:\$PATH && source ~/.zshrc && claude" +interactive_session "${LIGHTSAIL_SERVER_IP}" "source ~/.bashrc 2>/dev/null; source ~/.zshrc 2>/dev/null; claude" diff --git a/daytona/claude.sh b/daytona/claude.sh index a81a2a4f..ac466325 100644 --- a/daytona/claude.sh +++ b/daytona/claude.sh @@ -64,4 +64,4 @@ echo "" log_step "Starting Claude Code..." sleep 1 clear -interactive_session "export PATH=\$HOME/.local/bin:\$HOME/.bun/bin:\$PATH && source ~/.zshrc && claude" +interactive_session "source ~/.bashrc 2>/dev/null; source ~/.zshrc 2>/dev/null; claude" diff --git a/digitalocean/claude.sh b/digitalocean/claude.sh index 794aabdb..a4551c88 100755 --- a/digitalocean/claude.sh +++ b/digitalocean/claude.sh @@ -72,4 +72,4 @@ save_vm_connection "${DO_SERVER_IP}" "root" "${DO_DROPLET_ID}" "${DROPLET_NAME}" log_step "Starting Claude Code..." sleep 1 clear -interactive_session "${DO_SERVER_IP}" "export PATH=\$HOME/.local/bin:\$HOME/.bun/bin:\$PATH && source ~/.zshrc && claude" +interactive_session "${DO_SERVER_IP}" "source ~/.bashrc 2>/dev/null; source ~/.zshrc 2>/dev/null; claude" diff --git a/fly/claude.sh b/fly/claude.sh index 4277cad7..f259d6db 100644 --- a/fly/claude.sh +++ b/fly/claude.sh @@ -63,4 +63,4 @@ echo "" log_step "Starting Claude Code..." sleep 1 clear -interactive_session "export PATH=\$HOME/.local/bin:\$HOME/.bun/bin:\$PATH && source ~/.bashrc && claude" +interactive_session "source ~/.bashrc 2>/dev/null; source ~/.zshrc 2>/dev/null; claude" diff --git a/gcp/claude.sh b/gcp/claude.sh index b3de9ca5..f55d5817 100644 --- a/gcp/claude.sh +++ b/gcp/claude.sh @@ -72,4 +72,4 @@ echo "" log_step "Starting Claude Code..." sleep 1 clear -interactive_session "${GCP_SERVER_IP}" "export PATH=\$HOME/.local/bin:\$HOME/.bun/bin:\$PATH && source ~/.zshrc && claude" +interactive_session "${GCP_SERVER_IP}" "source ~/.bashrc 2>/dev/null; source ~/.zshrc 2>/dev/null; claude" diff --git a/hetzner/claude.sh b/hetzner/claude.sh index 7bdd9a8c..ec573f29 100755 --- a/hetzner/claude.sh +++ b/hetzner/claude.sh @@ -42,4 +42,4 @@ inject_env_vars_cb "$RUN" "$UPLOAD" \ # Claude-specific config setup_claude_code_config "${OPENROUTER_API_KEY}" "$UPLOAD" "$RUN" -launch_session "Hetzner server" "$SESSION" "export PATH=\$HOME/.claude/local/bin:\$HOME/.local/bin:\$HOME/.bun/bin:\$PATH && source ~/.zshrc && claude" +launch_session "Hetzner server" "$SESSION" "source ~/.bashrc 2>/dev/null; source ~/.zshrc 2>/dev/null; claude" diff --git a/oracle/claude.sh b/oracle/claude.sh index 7b15b56a..3755f6bf 100644 --- a/oracle/claude.sh +++ b/oracle/claude.sh @@ -72,4 +72,4 @@ echo "" log_step "Starting Claude Code..." sleep 1 clear -interactive_session "${OCI_SERVER_IP}" "export PATH=\$HOME/.local/bin:\$HOME/.bun/bin:\$PATH && source ~/.zshrc && claude" +interactive_session "${OCI_SERVER_IP}" "source ~/.bashrc 2>/dev/null; source ~/.zshrc 2>/dev/null; claude" diff --git a/ovh/claude.sh b/ovh/claude.sh index 732d6c54..0270497e 100644 --- a/ovh/claude.sh +++ b/ovh/claude.sh @@ -70,4 +70,4 @@ echo "" log_step "Starting Claude Code..." sleep 1 clear -interactive_session "${OVH_SERVER_IP}" "export PATH=\$HOME/.local/bin:\$HOME/.bun/bin:\$PATH && source ~/.zshrc && claude" +interactive_session "${OVH_SERVER_IP}" "source ~/.bashrc 2>/dev/null; source ~/.zshrc 2>/dev/null; claude" diff --git a/shared/common.sh b/shared/common.sh index c169e414..f07fdfdc 100644 --- a/shared/common.sh +++ b/shared/common.sh @@ -1263,9 +1263,20 @@ install_claude_code() { # Include fnm paths so node is found even in non-interactive SSH sessions local claude_path='export PATH=$HOME/.claude/local/bin:$HOME/.local/bin:$HOME/.bun/bin:$HOME/.local/share/fnm:$PATH; if command -v fnm >/dev/null 2>&1; then eval "$(fnm env)"; fi' + # Finalize installation: set up shell integration (PATH, completions) + # Also persists fnm bootstrap to shell configs so interactive sessions find node + _finalize_claude_install() { + log_step "Setting up Claude Code shell integration..." + ${run_cb} "${claude_path} && claude install --force" >/dev/null 2>&1 || true + # Ensure fnm bootstrap is in .bashrc and .zshrc so new shells can find node + local fnm_snippet='export PATH="\$HOME/.local/share/fnm:\$PATH"; if command -v fnm >/dev/null 2>&1; then eval "\$(fnm env)"; fi' + ${run_cb} "if command -v fnm >/dev/null 2>&1 || test -d \$HOME/.local/share/fnm; then for rc in ~/.bashrc ~/.zshrc; do grep -q 'fnm env' \"\$rc\" 2>/dev/null || printf '\\n# fnm (node version manager)\\n${fnm_snippet}\\n' >> \"\$rc\"; done; fi" >/dev/null 2>&1 || true + } + # Already installed? if ${run_cb} "${claude_path} && command -v claude && claude --version" >/dev/null 2>&1; then log_info "Claude Code already installed" + _finalize_claude_install return 0 fi @@ -1274,6 +1285,7 @@ install_claude_code() { if ${run_cb} "curl -fsSL https://claude.ai/install.sh | bash" 2>&1; then if ${run_cb} "${claude_path} && command -v claude && claude --version" >/dev/null 2>&1; then log_info "Claude Code installed via curl installer" + _finalize_claude_install return 0 fi log_warn "curl installer exited 0 but claude not found on PATH" @@ -1306,6 +1318,7 @@ install_claude_code() { if ${run_cb} "${claude_path} && npm install -g @anthropic-ai/claude-code 2>&1" 2>&1; then if ${run_cb} "${claude_path} && command -v claude && claude --version" >/dev/null 2>&1; then log_info "Claude Code installed via npm" + _finalize_claude_install return 0 fi log_warn "npm install exited 0 but claude binary not working" @@ -1318,6 +1331,7 @@ install_claude_code() { if ${run_cb} "${claude_path} && bun add -g @anthropic-ai/claude-code 2>&1" 2>&1; then if ${run_cb} "${claude_path} && command -v claude && claude --version" >/dev/null 2>&1; then log_info "Claude Code installed via bun" + _finalize_claude_install return 0 fi log_warn "bun install exited 0 but claude binary not working" diff --git a/sprite/claude.sh b/sprite/claude.sh index 2415c4d2..4e54b9ed 100755 --- a/sprite/claude.sh +++ b/sprite/claude.sh @@ -70,11 +70,11 @@ if [[ -n "${SPAWN_PROMPT:-}" ]]; then escaped_prompt=$(printf '%q' "${SPAWN_PROMPT}") # Execute without -tty flag - sprite exec -s "${SPRITE_NAME}" -- zsh -c "export PATH=\$HOME/.local/bin:\$HOME/.bun/bin:\$PATH && source ~/.zshrc && claude -p ${escaped_prompt}" + sprite exec -s "${SPRITE_NAME}" -- zsh -c "source ~/.zshrc 2>/dev/null; claude -p ${escaped_prompt}" else # Interactive mode: start Claude Code normally log_step "Starting Claude Code..." sleep 1 clear 2>/dev/null || true - sprite exec -s "${SPRITE_NAME}" -tty -- zsh -c "export PATH=\$HOME/.local/bin:\$HOME/.bun/bin:\$PATH && source ~/.zshrc && claude" + sprite exec -s "${SPRITE_NAME}" -tty -- zsh -c "source ~/.zshrc 2>/dev/null; claude" fi