install.sh•6.74 kB
#!/usr/bin/env bash
set -euo pipefail
# Remote installer for Claude Code Local Context
# Usage:
# curl -fsSL https://raw.githubusercontent.com/FarhanAliRaza/claude-context-local/main/scripts/install.sh | bash
REPO_URL="https://github.com/FarhanAliRaza/claude-context-local"
PROJECT_DIR="${HOME}/.local/share/claude-context-local"
STORAGE_DIR="${HOME}/.claude_code_search"
MODEL_NAME="google/embeddinggemma-300m"
print() { printf "%b\n" "$1"; }
hr() { print "\n==================================================\n"; }
hr; print "Installing Claude Context Local"; hr
# 1) Ensure git is available
if ! command -v git >/dev/null 2>&1; then
print "ERROR: git is required. Please install git and re-run."; exit 1
fi
# 2) Ensure uv is available (prefer uv as per project policy)
if ! command -v uv >/dev/null 2>&1; then
print "uv not found. Installing uv..."
# Attempt installing uv
curl -LsSf https://astral.sh/uv/install.sh | sh
if ! command -v uv >/dev/null 2>&1; then
print "ERROR: uv installation failed or not found in PATH."; exit 1
fi
fi
# 3) Clone or update repository
mkdir -p "${PROJECT_DIR}"
IS_UPDATE=0
if [[ -d "${PROJECT_DIR}/.git" ]]; then
print "Found existing installation at ${PROJECT_DIR}"
IS_UPDATE=1
# Check if there are uncommitted changes
if ! git -C "${PROJECT_DIR}" diff-index --quiet HEAD -- 2>/dev/null; then
print "WARNING: You have uncommitted changes in ${PROJECT_DIR}"
# When piped (curl | bash), auto-select update
if [ -t 0 ]; then
# Interactive terminal - ask user
printf "Options:\n [u] Update anyway (stash changes)\n [k] Keep current version\n [d] Delete and reinstall\nChoice [u/k/d]: "
read -r choice
else
# Piped input - default to update
print "Auto-selecting: Update anyway (stash changes)"
choice="u"
fi
case "${choice}" in
k|K) print "Keeping current installation. Skipping git update."; SKIP_UPDATE=1 ;;
d|D)
print "Removing ${PROJECT_DIR} for clean reinstall..."
rm -rf "${PROJECT_DIR}"
git clone "${REPO_URL}" "${PROJECT_DIR}"
IS_UPDATE=0 # Treat as fresh install
;;
u|U|*)
print "Stashing changes and updating..."
git -C "${PROJECT_DIR}" stash push -m "Auto-stash before installer update $(date)"
git -C "${PROJECT_DIR}" remote set-url origin "${REPO_URL}"
git -C "${PROJECT_DIR}" fetch --tags --prune
git -C "${PROJECT_DIR}" pull --ff-only
print "Your changes are stashed. Run 'git stash pop' in ${PROJECT_DIR} to restore them."
;;
esac
else
print "Updating repository..."
git -C "${PROJECT_DIR}" remote set-url origin "${REPO_URL}"
git -C "${PROJECT_DIR}" fetch --tags --prune
git -C "${PROJECT_DIR}" pull --ff-only
fi
else
print "Cloning ${REPO_URL} to ${PROJECT_DIR}"
git clone "${REPO_URL}" "${PROJECT_DIR}"
IS_UPDATE=0
fi
# 4) Install Python dependencies
if [[ "${SKIP_UPDATE:-0}" != "1" ]]; then
print "Installing Python dependencies with uv"
(cd "${PROJECT_DIR}" && uv sync)
else
print "Skipping dependency update (keeping current version)"
fi
# 5) Prefer FAISS GPU wheels on NVIDIA machines
# Only skip GPU detection if SKIP_GPU is explicitly set
if [[ "${SKIP_GPU:-0}" == "1" ]]; then
print "Skipping GPU detection (SKIP_GPU=1). Using faiss-cpu."
else
print "Checking for NVIDIA GPU to install FAISS GPU wheels (optional)"
(
set +e
GPU_DETECTED=0
if command -v nvidia-smi >/dev/null 2>&1; then
GPU_DETECTED=1
fi
if [[ "${GPU_DETECTED}" -eq 1 ]]; then
echo "NVIDIA GPU detected. Attempting to install faiss-gpu wheels..."
# Try CUDA 12 wheels first, then CUDA 11, fallback to CPU
if (cd "${PROJECT_DIR}" && uv add faiss-gpu-cu12) >/dev/null 2>&1; then
echo "✓ Installed faiss-gpu-cu12"
(cd "${PROJECT_DIR}" && uv remove faiss-cpu) >/dev/null 2>&1 || true
elif (cd "${PROJECT_DIR}" && uv add faiss-gpu-cu11) >/dev/null 2>&1; then
echo "✓ Installed faiss-gpu-cu11"
(cd "${PROJECT_DIR}" && uv remove faiss-cpu) >/dev/null 2>&1 || true
else
echo "Could not install faiss-gpu wheels. Keeping CPU build."
fi
else
echo "No NVIDIA GPU detected. Using faiss-cpu (default)."
fi
set -e
)
fi
# 6) Download model to storage dir
print "Downloading embedding model to ${STORAGE_DIR}"
mkdir -p "${STORAGE_DIR}"
(cd "${PROJECT_DIR}" && uv run scripts/download_model_standalone.py --storage-dir "${STORAGE_DIR}" --model "${MODEL_NAME}" -v)
# Colors for better visibility
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
BOLD='\033[1m'
NC='\033[0m' # No Color
if [[ "${IS_UPDATE}" -eq 1 ]]; then
hr; printf "${GREEN}${BOLD}✅ Update complete!${NC}\n"; hr
printf "${BLUE}📍 Locations:${NC}\n"
printf " Project: ${PROJECT_DIR}\n"
printf " Storage: ${STORAGE_DIR} ${GREEN}(embeddings preserved ✓)${NC}\n\n"
printf "${RED}${BOLD}🔄 IMPORTANT: Re-register MCP server after updates${NC}\n"
printf "${YELLOW}Run these commands:${NC}\n\n"
printf "${BOLD}1) Remove old server:${NC}\n"
printf " ${BLUE}claude mcp remove code-search${NC}\n\n"
printf "${BOLD}2) Add updated server:${NC}\n"
printf " ${BLUE}claude mcp add code-search --scope user -- uv run --directory ${PROJECT_DIR} python mcp_server/server.py${NC}\n\n"
printf "${BOLD}3) Verify connection:${NC}\n"
printf " ${BLUE}claude mcp list${NC}\n"
printf " ${GREEN}Look for: code-search ... ✓ Connected${NC}\n\n"
printf "${BOLD}4) Then in Claude Code:${NC}\n"
printf " ${BLUE}index this codebase${NC}\n\n"
printf "${YELLOW}💡 Notes:${NC}\n"
printf "%s\n" "• Your embeddings and indexed projects are preserved"
printf "%s\n" "• Only the code was updated; your data remains intact"
else
hr; printf "${GREEN}${BOLD}✅ Install complete!${NC}\n"; hr
printf "${BLUE}📍 Locations:${NC}\n"
printf " Project: ${PROJECT_DIR}\n"
printf " Storage: ${STORAGE_DIR}\n\n"
printf "${BOLD}Next steps:${NC}\n\n"
printf "${BOLD}1) Add MCP server to Claude Code:${NC}\n"
printf " ${BLUE}claude mcp add code-search --scope user -- uv run --directory ${PROJECT_DIR} python mcp_server/server.py${NC}\n\n"
printf "${BOLD}2) Verify connection:${NC}\n"
printf " ${BLUE}claude mcp list${NC}\n"
printf " ${GREEN}Look for: code-search ... ✓ Connected${NC}\n\n"
printf "${BOLD}3) Then in Claude Code:${NC}\n"
printf " ${BLUE}index this codebase${NC}\n\n"
printf "${YELLOW}💡 Notes:${NC}\n"
printf "%s\n" "• To update later, re-run this installer"
printf "%s\n" "• Your embeddings will be stored in ${STORAGE_DIR}"
fi