#!/bin/bash # ============================================================================================== # ================================= Claude Code Startup ======================================== # ============================================================================================== # # PURPOSE # ───────────────────────────────────────────────────────────────────────────── # Restores Claude Code's persistent data after an Unraid reboot. # /root is RAM — wiped on every boot. This script re-creates symlinks so # Claude's memory, sessions, settings, and binary survive across reboots. # # Storage mode is read from the host conf file: # # HOST*_STORAGE_MODE_INTERNAL=true → Internal (boot) mode # .claude data → /boot/config/claude # binary → /boot/config/claude-bin # No array dependency — runs even before array mounts. # # HOST*_STORAGE_MODE_INTERNAL=false → Appdata mode # .claude data → /mnt/user/appdata/claude-code/.claude # binary → /mnt/user/appdata/claude-code/local/share/claude # Requires array to be mounted. # # On first run in either mode, migrates any existing live data to persistent # storage. Subsequent runs only re-create the symlinks. # # Standalone script — no common.sh dependency. Safe to run directly from # terminal or from array_started.sh. # # ============================================================================================== # RUNTIME MODES # ============================================================================================== # # claude_startup.sh # Set up persistent symlinks only — default, used by array_started.sh on boot. # # claude_startup.sh --launch # Set up persistent symlinks and launch Claude interactively. # # ============================================================================================== LAUNCH=false [[ "$1" == "--launch" ]] && LAUNCH=true _log() { echo " ✅ $*"; } _warn() { echo " ⚠️ $*"; } _err() { echo " ❌ $*" >&2; } echo "" echo "━━━ Claude Code Startup ━━━" echo "" # ── Detect storage mode ─────────────────────────────────────────────────────────────────────── CONF_DIR="/boot/config/plugins/varaverk/Configurations" STORAGE_INTERNAL=false for _conf in "$CONF_DIR"/host*.conf; do [[ -f "$_conf" ]] || continue if grep -q "_STORAGE_MODE_INTERNAL=true" "$_conf" 2>/dev/null; then STORAGE_INTERNAL=true break fi done if [[ "$STORAGE_INTERNAL" == true ]]; then CLAUDE_DATA="/boot/config/plugins/varaverk/claude-data" CLAUDE_BIN="/boot/config/plugins/varaverk/claude-bin" _log "Storage mode: internal boot" else PERSIST_DIR="/mnt/user/appdata/claude-code" CLAUDE_DATA="$PERSIST_DIR/.claude" CLAUDE_BIN="$PERSIST_DIR/local/share/claude" _log "Storage mode: appdata" fi # ── Array check (appdata mode only) ────────────────────────────────────────────────────────── if [[ "$STORAGE_INTERNAL" == false ]]; then if ! mountpoint -q /mnt/user 2>/dev/null; then _err "Array not mounted — /mnt/user not available (required for appdata mode)" exit 1 fi fi # ── Create persistent dirs ──────────────────────────────────────────────────────────────────── mkdir -p "$CLAUDE_DATA" "$CLAUDE_BIN" # ── Migrate from old /boot/config/claude location (internal mode only) ─────────────────────── if [[ "$STORAGE_INTERNAL" == true && -d /boot/config/claude && "$CLAUDE_DATA" != "/boot/config/claude" ]]; then if [[ -z "$(ls -A "$CLAUDE_DATA" 2>/dev/null)" ]]; then _warn "Migrating old /boot/config/claude → $CLAUDE_DATA" cp -a /boot/config/claude/. "$CLAUDE_DATA/" _log "Migrated .claude data from old location" fi fi # ── Migrate .claude on first run ────────────────────────────────────────────────────────────── if [[ ! -L /root/.claude && -d /root/.claude ]]; then _warn "First run — migrating /root/.claude → $CLAUDE_DATA" cp -a /root/.claude/. "$CLAUDE_DATA/" rm -rf /root/.claude _log "Migrated .claude (memory, sessions, settings)" elif [[ -z "$(ls -A "$CLAUDE_DATA" 2>/dev/null)" && -d /root/.claude ]]; then _warn "Persistent storage empty — copying current .claude data" cp -a /root/.claude/. "$CLAUDE_DATA/" _log "Copied .claude data to persistent storage" fi # ── Migrate Claude binaries on first run ────────────────────────────────────────────────────── if [[ ! -L /root/.local/share/claude && -d /root/.local/share/claude ]]; then _warn "First run — migrating Claude binaries → $CLAUDE_BIN" cp -a /root/.local/share/claude/. "$CLAUDE_BIN/" _log "Migrated Claude binaries" fi # ── Create symlinks ─────────────────────────────────────────────────────────────────────────── mkdir -p /root/.local/share /root/.local/bin [[ -d /root/.claude && ! -L /root/.claude ]] && rm -rf /root/.claude ln -sfn "$CLAUDE_DATA" /root/.claude _log ".claude → $CLAUDE_DATA" [[ -d /root/.local/share/claude && ! -L /root/.local/share/claude ]] && rm -rf /root/.local/share/claude ln -sfn "$CLAUDE_BIN" /root/.local/share/claude _log "claude binary → $CLAUDE_BIN" # ── Symlink CLAUDE.md ───────────────────────────────────────────────────────────────────────── CLAUDE_MD="/boot/config/plugins/varaverk/CLAUDE.md" if [[ -f "$CLAUDE_MD" ]]; then ln -sfn "$CLAUDE_MD" /root/CLAUDE.md _log "CLAUDE.md → $CLAUDE_MD" else _warn "CLAUDE.md not found at $CLAUDE_MD — skipping symlink" fi # ── Point the claude binary at the latest installed version ─────────────────────────────────── LATEST=$(ls "$CLAUDE_BIN/versions/" 2>/dev/null | sort -V | tail -1) if [[ -z "$LATEST" ]]; then _err "No Claude versions found in $CLAUDE_BIN/versions/" _err "Install Claude Code first: npm install -g @anthropic-ai/claude-code" exit 1 fi ln -sfn "$CLAUDE_BIN/versions/$LATEST" /root/.local/bin/claude _log "claude v$LATEST ready" echo "" if [[ "$LAUNCH" == false ]]; then _log "Setup complete — run 'claude' to start" echo "" exit 0 fi # ── Launch ──────────────────────────────────────────────────────────────────────────────────── export PATH="$HOME/.local/bin:$PATH" exec claude