From 3c6ad62ed07dcce92f0ea91c26dd092f7e4ae677 Mon Sep 17 00:00:00 2001 From: Martin Vogel Date: Wed, 25 Feb 2026 10:07:45 +0100 Subject: [PATCH] Add cross-platform setup scripts and CI release workflow - GitHub Actions workflow builds native binaries for 5 platforms: darwin-arm64, darwin-amd64, linux-amd64, linux-arm64, windows-amd64 - Windows build uses MSYS2 UCRT64 toolchain for CGO compatibility - setup.sh: macOS/Linux installer (binary download or --from-source) - setup-windows.ps1: Windows installer (native .exe or -FromSource via WSL) - Both scripts offer interactive Claude Code MCP auto-configuration - Release includes checksums.txt with SHA-256 hashes - PS5.1-compatible JSON handling (no -AsHashtable dependency) --- .github/workflows/release.yml | 123 +++++++++++++ scripts/setup-windows.ps1 | 326 ++++++++++++++++++++++++++++++++++ scripts/setup.sh | 324 +++++++++++++++++++++++++++++++++ 3 files changed, 773 insertions(+) create mode 100644 .github/workflows/release.yml create mode 100644 scripts/setup-windows.ps1 create mode 100755 scripts/setup.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..225b36ac6 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,123 @@ +name: Release + +on: + workflow_dispatch: + inputs: + version: + description: "Release version (e.g. v0.0.2)" + required: true + type: string + +permissions: + contents: write + +jobs: + build-unix: + strategy: + matrix: + include: + - os: ubuntu-latest + goos: linux + goarch: amd64 + - os: ubuntu-24.04-arm + goos: linux + goarch: arm64 + - os: macos-14 + goos: darwin + goarch: arm64 + - os: macos-13 + goos: darwin + goarch: amd64 + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v5 + with: + go-version: "1.23" + + - name: Run tests + run: go test ./... + + - name: Build binary + run: | + CGO_ENABLED=1 go build \ + -ldflags "-X main.version=${{ inputs.version }}" \ + -o codebase-memory-mcp-${{ matrix.goos }}-${{ matrix.goarch }} \ + ./cmd/codebase-memory-mcp/ + + - name: Create archive + run: tar -czf codebase-memory-mcp-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz codebase-memory-mcp-${{ matrix.goos }}-${{ matrix.goarch }} + + - uses: actions/upload-artifact@v4 + with: + name: codebase-memory-mcp-${{ matrix.goos }}-${{ matrix.goarch }} + path: "*.tar.gz" + + build-windows: + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v5 + with: + go-version: "1.23" + + - uses: msys2/setup-msys2@v2 + with: + msystem: UCRT64 + path-type: inherit + install: mingw-w64-ucrt-x86_64-gcc + + - name: Run tests + shell: msys2 {0} + run: | + which go + go version + go test ./... + + - name: Build binary + shell: msys2 {0} + run: | + CGO_ENABLED=1 CC=gcc go build \ + -ldflags "-X main.version=${{ inputs.version }}" \ + -o codebase-memory-mcp-windows-amd64.exe \ + ./cmd/codebase-memory-mcp/ + + - name: Create archive + shell: pwsh + run: Compress-Archive -Path codebase-memory-mcp-windows-amd64.exe -DestinationPath codebase-memory-mcp-windows-amd64.zip + + - uses: actions/upload-artifact@v4 + with: + name: codebase-memory-mcp-windows-amd64 + path: "*.zip" + + release: + needs: [build-unix, build-windows] + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + + - uses: actions/download-artifact@v4 + with: + merge-multiple: true + + - name: Generate checksums + run: sha256sum *.tar.gz *.zip > checksums.txt + + - name: Create tag + run: | + git tag ${{ inputs.version }} + git push origin ${{ inputs.version }} + + - uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ inputs.version }} + files: | + *.tar.gz + *.zip + checksums.txt + generate_release_notes: true diff --git a/scripts/setup-windows.ps1 b/scripts/setup-windows.ps1 new file mode 100644 index 000000000..b8daaf0cf --- /dev/null +++ b/scripts/setup-windows.ps1 @@ -0,0 +1,326 @@ +# codebase-memory-mcp setup script (Windows) +# Default: download pre-built native Windows binary +# -FromSource: build from source inside WSL (requires Go + gcc in WSL) + +param( + [switch]$FromSource, + [switch]$Help +) + +$ErrorActionPreference = "Stop" + +$Repo = "DeusData/codebase-memory-mcp" +$BinaryName = "codebase-memory-mcp" +$InstallDir = Join-Path $env:LOCALAPPDATA "codebase-memory-mcp" + +# --- Helpers --- + +function Write-Ok($msg) { Write-Host " $msg" -ForegroundColor Green } +function Write-Fail($msg) { Write-Host " $msg" -ForegroundColor Red } +function Write-Warn($msg) { Write-Host " $msg" -ForegroundColor Yellow } + +function Read-SettingsJson($Path) { + # PS5.1-compatible: ConvertFrom-Json returns PSCustomObject, not Hashtable. + # We convert to ordered hashtable manually. + if (-not (Test-Path $Path)) { + return @{} + } + $raw = Get-Content $Path -Raw + if (-not $raw -or $raw.Trim() -eq "") { + return @{} + } + $obj = $raw | ConvertFrom-Json + $ht = [ordered]@{} + foreach ($prop in $obj.PSObject.Properties) { + if ($prop.Value -is [System.Management.Automation.PSCustomObject]) { + $inner = [ordered]@{} + foreach ($p in $prop.Value.PSObject.Properties) { + $inner[$p.Name] = $p.Value + } + $ht[$prop.Name] = $inner + } else { + $ht[$prop.Name] = $prop.Value + } + } + return $ht +} + +function Write-SettingsJson($Path, $Settings) { + # Back up existing file before writing + if (Test-Path $Path) { + Copy-Item $Path "$Path.bak" -Force + } + $Settings | ConvertTo-Json -Depth 10 | Set-Content $Path -Encoding UTF8 +} + +function Configure-ClaudeCode($McpConfig) { + Write-Host "" + $answer = Read-Host "Configure Claude Code to use codebase-memory-mcp? [y/N]" + + if ($answer -match '^[Yy]$') { + $settingsPath = Join-Path $env:USERPROFILE ".claude\settings.json" + $settingsDir = Split-Path $settingsPath -Parent + + if (-not (Test-Path $settingsDir)) { + New-Item -ItemType Directory -Path $settingsDir -Force | Out-Null + } + + $settings = Read-SettingsJson $settingsPath + + if (-not $settings.Contains("mcpServers")) { + $settings["mcpServers"] = [ordered]@{} + } + + $settings["mcpServers"]["codebase-memory-mcp"] = $McpConfig + Write-SettingsJson $settingsPath $settings + Write-Ok "Updated $settingsPath" + } else { + Write-Host "" + Write-Host " Add this to your .mcp.json or %USERPROFILE%\.claude\settings.json:" -ForegroundColor White + Write-Host "" + $snippet = @{ mcpServers = @{ "codebase-memory-mcp" = $McpConfig } } + $snippet | ConvertTo-Json -Depth 10 | Write-Host + } +} + +function Test-WSL { + try { + $null = wsl.exe --status 2>&1 + if ($LASTEXITCODE -ne 0) { return $false } + return $true + } catch { + return $false + } +} + +function Get-WSLDistro { + $output = wsl.exe -l -v 2>&1 | Out-String + $lines = $output -split "`n" | Where-Object { $_ -match '\S' } | Select-Object -Skip 1 + foreach ($line in $lines) { + $clean = $line -replace '\x00', '' -replace '^\s+', '' + if ($clean -match '^\*?\s*(\S+)\s+') { + $name = $Matches[1] + if ($name -ne "NAME" -and $name -ne "") { + return $name + } + } + } + return $null +} + +function Invoke-WSL { + param([string]$Command) + $result = wsl.exe -- bash -c $Command 2>&1 + if ($LASTEXITCODE -ne 0) { + throw "WSL command failed: $Command`n$result" + } + return $result +} + +# --- Main --- + +if ($Help) { + Write-Host "" + Write-Host "Usage: .\setup-windows.ps1 [-FromSource] [-Help]" + Write-Host "" + Write-Host " Default: Download pre-built Windows binary" + Write-Host " -FromSource: Build from source inside WSL (requires Go 1.23+ and gcc in WSL)" + Write-Host "" + exit 0 +} + +Write-Host "" +Write-Host "codebase-memory-mcp installer (Windows)" -ForegroundColor White +Write-Host "" + +if ($FromSource) { + # --- Build from source via WSL --- + Write-Host "Checking WSL2 (required for building from source)..." -ForegroundColor White + + if (-not (Test-WSL)) { + Write-Fail "WSL2 is not available. Required for building from source (CGO needs gcc)." + Write-Host "" + Write-Host " Install WSL2:" -ForegroundColor Yellow + Write-Host " wsl --install -d Ubuntu" + Write-Host " Then restart your computer and run this script again." + Write-Host "" + Write-Host " Alternatively, download a pre-built binary (no WSL needed):" -ForegroundColor Yellow + Write-Host " .\setup-windows.ps1" + exit 1 + } + Write-Ok "WSL2 is available" + + $distro = Get-WSLDistro + if (-not $distro) { + Write-Fail "No WSL Linux distribution found." + Write-Host "" + Write-Host " Install Ubuntu:" -ForegroundColor Yellow + Write-Host " wsl --install -d Ubuntu" + exit 1 + } + Write-Ok "WSL distro: $distro" + + $wslUser = (Invoke-WSL "whoami").Trim() + Write-Ok "WSL user: $wslUser" + + Write-Host "" + Write-Host "Checking prerequisites inside WSL..." -ForegroundColor White + + # Check for gcc + try { + Invoke-WSL "command -v gcc" | Out-Null + Write-Ok "gcc found" + } catch { + Write-Warn "gcc not found. Installing build-essential..." + Invoke-WSL "sudo apt-get update && sudo apt-get install -y build-essential" + } + + # Check for Go + try { + $goVersion = Invoke-WSL "go version" + Write-Ok "Go: $goVersion" + } catch { + Write-Fail "Go not found in WSL." + Write-Host "" + Write-Host " Install Go 1.23+ inside WSL:" -ForegroundColor Yellow + Write-Host " See https://go.dev/dl/ for Linux amd64 tarball" + exit 1 + } + + # Check for git + try { + Invoke-WSL "command -v git" | Out-Null + Write-Ok "git found" + } catch { + Write-Fail "git not found in WSL. Install with: sudo apt install git" + exit 1 + } + + # Clone or update + Write-Host "" + $sourceDir = "/home/$wslUser/.local/share/codebase-memory-mcp" + try { + Invoke-WSL "test -d $sourceDir/.git" | Out-Null + Write-Host "Updating source..." -ForegroundColor White + Invoke-WSL "git -C $sourceDir pull --ff-only" + } catch { + Write-Host "Cloning repository..." -ForegroundColor White + Invoke-WSL "mkdir -p /home/$wslUser/.local/share && git clone https://github.com/$Repo.git $sourceDir" + } + Write-Ok "Source at $sourceDir" + + # Build + Write-Host "" + Write-Host "Building binary (this may take a minute)..." -ForegroundColor White + $wslBinaryPath = "/home/$wslUser/.local/bin/$BinaryName" + Invoke-WSL "mkdir -p /home/$wslUser/.local/bin && cd $sourceDir && CGO_ENABLED=1 go build -buildvcs=false -o $wslBinaryPath ./cmd/codebase-memory-mcp/" + Write-Ok "Built to $wslBinaryPath (inside WSL)" + + # Verify + try { + Invoke-WSL "test -x $wslBinaryPath" | Out-Null + Write-Ok "Binary is executable" + } catch { + Write-Fail "Binary at $wslBinaryPath is not executable" + exit 1 + } + + # Configure — WSL binary needs wsl.exe wrapper + $mcpConfig = [ordered]@{ + type = "stdio" + command = "wsl.exe" + args = @("-d", $distro, "--", $wslBinaryPath) + } + + Configure-ClaudeCode $mcpConfig + + Write-Host "" + Write-Ok "Done! Restart Claude Code and verify with /mcp" + Write-Host "" + Write-Host " To uninstall:" -ForegroundColor White + Write-Host " wsl.exe -- rm $wslBinaryPath" + Write-Host " wsl.exe -- rm -rf $sourceDir" + Write-Host " wsl.exe -- rm -rf ~/.cache/codebase-memory-mcp/" + +} else { + # --- Download pre-built native Windows binary --- + Write-Host "Fetching latest release..." -ForegroundColor White + + $releaseUrl = "https://api.github.com/repos/$Repo/releases/latest" + $release = Invoke-RestMethod -Uri $releaseUrl -Headers @{ "User-Agent" = "codebase-memory-mcp-setup" } + $tag = $release.tag_name + + if (-not $tag) { + Write-Fail "Could not determine latest release." + Write-Host " Check: https://github.com/$Repo/releases" + exit 1 + } + Write-Ok "Latest release: $tag" + + $asset = "codebase-memory-mcp-windows-amd64.zip" + $downloadUrl = "https://github.com/$Repo/releases/download/$tag/$asset" + + Write-Host "Downloading $asset..." -ForegroundColor White + + # Create install directory + if (-not (Test-Path $InstallDir)) { + New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null + } + + $tmpZip = Join-Path $env:TEMP $asset + Invoke-WebRequest -Uri $downloadUrl -OutFile $tmpZip -UseBasicParsing + + # Extract + Expand-Archive -Path $tmpZip -DestinationPath $InstallDir -Force + Remove-Item $tmpZip -Force + + $binaryPath = Join-Path $InstallDir "$BinaryName.exe" + + if (-not (Test-Path $binaryPath)) { + Write-Fail "Binary not found at $binaryPath after extraction" + exit 1 + } + Write-Ok "Installed to $binaryPath" + + # Verify binary runs + try { + $verOut = & $binaryPath --version 2>&1 + Write-Ok "Version: $verOut" + } catch { + Write-Warn "Could not verify binary version (may still work)" + } + + # SmartScreen note + Write-Host "" + Write-Warn "Windows SmartScreen may show a warning when the binary runs for the first time." + Write-Host " This is normal for unsigned open-source binaries." -ForegroundColor Yellow + Write-Host " Click 'More info' then 'Run anyway' to proceed." -ForegroundColor Yellow + Write-Host " Verify checksums at: https://github.com/$Repo/releases" -ForegroundColor Yellow + + # Check if install dir is on PATH + $userPath = [Environment]::GetEnvironmentVariable("Path", "User") + if ($userPath -notlike "*$InstallDir*") { + Write-Host "" + Write-Warn "$InstallDir is not on your PATH." + $addPath = Read-Host " Add it to your user PATH? [y/N]" + if ($addPath -match '^[Yy]$') { + [Environment]::SetEnvironmentVariable("Path", "$userPath;$InstallDir", "User") + Write-Ok "Added to user PATH (restart your terminal to take effect)" + } + } + + # Configure Claude Code + $mcpConfig = [ordered]@{ + type = "stdio" + command = $binaryPath + } + + Configure-ClaudeCode $mcpConfig + + Write-Host "" + Write-Ok "Done! Restart Claude Code and verify with /mcp" + Write-Host "" + Write-Host " To uninstall:" -ForegroundColor White + Write-Host " Remove-Item -Recurse -Force '$InstallDir'" + Write-Host " Remove-Item -Recurse -Force `"$env:LOCALAPPDATA\codebase-memory-mcp`" # graph database" +} diff --git a/scripts/setup.sh b/scripts/setup.sh new file mode 100755 index 000000000..70ee35c41 --- /dev/null +++ b/scripts/setup.sh @@ -0,0 +1,324 @@ +#!/usr/bin/env bash +set -euo pipefail + +# codebase-memory-mcp setup script (macOS + Linux) +# Default: download pre-built binary from GitHub Release +# --from-source: build from source (requires Go + C compiler) + +REPO="DeusData/codebase-memory-mcp" +INSTALL_DIR="$HOME/.local/bin" +BINARY_NAME="codebase-memory-mcp" +SOURCE_DIR="$HOME/.local/share/codebase-memory-mcp" + +# --- Colors --- + +if [ -t 1 ] && command -v tput &>/dev/null; then + GREEN=$(tput setaf 2) + RED=$(tput setaf 1) + YELLOW=$(tput setaf 3) + BOLD=$(tput bold) + RESET=$(tput sgr0) +else + GREEN="" + RED="" + YELLOW="" + BOLD="" + RESET="" +fi + +ok() { echo "${GREEN}✓${RESET} $*"; } +fail() { echo "${RED}✗${RESET} $*"; } +warn() { echo "${YELLOW}⚠${RESET} $*"; } +info() { echo " $*"; } + +die() { fail "$@"; exit 1; } + +# --- Argument parsing --- + +FROM_SOURCE=false +for arg in "$@"; do + case "$arg" in + --from-source) FROM_SOURCE=true ;; + --help|-h) + echo "Usage: $0 [--from-source]" + echo "" + echo " Default: Download pre-built binary from GitHub Release" + echo " --from-source: Clone and build from source (requires Go 1.23+ and a C compiler)" + exit 0 + ;; + *) die "Unknown argument: $arg" ;; + esac +done + +# --- Platform detection --- + +detect_platform() { + local os arch + os=$(uname -s) + arch=$(uname -m) + + case "$os" in + Darwin) os="darwin" ;; + Linux) os="linux" ;; + *) die "Unsupported OS: $os. Use WSL2 on Windows." ;; + esac + + case "$arch" in + arm64|aarch64) arch="arm64" ;; + x86_64|amd64) arch="amd64" ;; + *) die "Unsupported architecture: $arch" ;; + esac + + echo "${os}-${arch}" +} + +# --- Prerequisite checks --- + +check_download_tool() { + if command -v curl &>/dev/null; then + echo "curl" + elif command -v wget &>/dev/null; then + echo "wget" + else + die "Neither curl nor wget found. Install one and retry." + fi +} + +check_go_version() { + if ! command -v go &>/dev/null; then + die "Go not found. Install Go 1.23+ from https://go.dev/dl/" + fi + + local version + version=$(go version | grep -oE 'go[0-9]+\.[0-9]+' | head -1) + local major minor + major=$(echo "$version" | grep -oE '[0-9]+' | head -1) + minor=$(echo "$version" | grep -oE '[0-9]+' | sed -n '2p') + + if [ "$major" -lt 1 ] || { [ "$major" -eq 1 ] && [ "$minor" -lt 23 ]; }; then + die "Go $major.$minor found, but 1.23+ is required. Update from https://go.dev/dl/" + fi + + ok "Go $major.$minor" +} + +check_c_compiler() { + if command -v cc &>/dev/null || command -v gcc &>/dev/null || command -v clang &>/dev/null; then + ok "C compiler found" + return + fi + + local os + os=$(uname -s) + if [ "$os" = "Darwin" ]; then + die "No C compiler found. Run: xcode-select --install" + else + die "No C compiler found. Install build-essential (Debian/Ubuntu) or gcc (Fedora/RHEL)" + fi +} + +check_git() { + if ! command -v git &>/dev/null; then + die "Git not found. Install git and retry." + fi + ok "Git found" +} + +# --- Download binary --- + +fetch() { + local url="$1" tool="$2" + if [ "$tool" = "curl" ]; then + curl -fsSL "$url" + else + wget -qO- "$url" + fi +} + +download_binary() { + local platform="$1" tool="$2" + + echo "" + echo "${BOLD}Fetching latest release...${RESET}" + local tag + tag=$(fetch "https://api.github.com/repos/${REPO}/releases/latest" "$tool" | grep '"tag_name"' | head -1 | sed 's/.*"tag_name": *"//;s/".*//') + + if [ -z "$tag" ]; then + die "Could not determine latest release. Check https://github.com/${REPO}/releases" + fi + ok "Latest release: $tag" + + local asset="codebase-memory-mcp-${platform}.tar.gz" + local url="https://github.com/${REPO}/releases/download/${tag}/${asset}" + + echo "${BOLD}Downloading ${asset}...${RESET}" + local tmpdir + tmpdir=$(mktemp -d) + trap 'rm -rf "$tmpdir"' EXIT + + fetch "$url" "$tool" > "${tmpdir}/${asset}" + tar -xzf "${tmpdir}/${asset}" -C "$tmpdir" + + mkdir -p "$INSTALL_DIR" + mv "${tmpdir}/codebase-memory-mcp-${platform}" "${INSTALL_DIR}/${BINARY_NAME}" + chmod +x "${INSTALL_DIR}/${BINARY_NAME}" + + ok "Installed to ${INSTALL_DIR}/${BINARY_NAME}" +} + +# --- Build from source --- + +build_from_source() { + echo "" + echo "${BOLD}Checking prerequisites...${RESET}" + check_go_version + check_c_compiler + check_git + + echo "" + if [ -d "$SOURCE_DIR/.git" ]; then + echo "${BOLD}Updating source...${RESET}" + git -C "$SOURCE_DIR" pull --ff-only + else + echo "${BOLD}Cloning repository...${RESET}" + mkdir -p "$(dirname "$SOURCE_DIR")" + git clone "https://github.com/${REPO}.git" "$SOURCE_DIR" + fi + ok "Source at ${SOURCE_DIR}" + + echo "" + echo "${BOLD}Building binary (this may take a minute)...${RESET}" + mkdir -p "$INSTALL_DIR" + + local build_flags="" + # Use -buildvcs=false in WSL (git metadata fails across filesystem boundaries) + if grep -qi microsoft /proc/version 2>/dev/null; then + build_flags="-buildvcs=false" + fi + + (cd "$SOURCE_DIR" && CGO_ENABLED=1 go build $build_flags -o "${INSTALL_DIR}/${BINARY_NAME}" ./cmd/codebase-memory-mcp/) + + ok "Built and installed to ${INSTALL_DIR}/${BINARY_NAME}" +} + +# --- MCP auto-configuration --- + +configure_claude() { + echo "" + local binary_path="${INSTALL_DIR}/${BINARY_NAME}" + local settings_file="$HOME/.claude/settings.json" + + printf "%s" "${BOLD}Configure Claude Code to use codebase-memory-mcp? [y/N] ${RESET}" + read -r answer + if [[ ! "$answer" =~ ^[Yy]$ ]]; then + echo "" + info "Add this to your .mcp.json or ~/.claude/settings.json:" + echo "" + echo ' {' + echo ' "mcpServers": {' + echo ' "codebase-memory-mcp": {' + echo ' "type": "stdio",' + echo " \"command\": \"${binary_path}\"" + echo ' }' + echo ' }' + echo ' }' + return + fi + + local mcp_entry + mcp_entry=$(cat </dev/null; then + # Use jq to merge + if [ -f "$settings_file" ]; then + local tmp + tmp=$(mktemp) + jq --argjson entry "$mcp_entry" '.mcpServers["codebase-memory-mcp"] = $entry' "$settings_file" > "$tmp" + mv "$tmp" "$settings_file" + else + echo "{}" | jq --argjson entry "$mcp_entry" '.mcpServers["codebase-memory-mcp"] = $entry' > "$settings_file" + fi + ok "Updated ${settings_file}" + elif command -v python3 &>/dev/null; then + python3 -c " +import json, os +path = os.path.expanduser('$settings_file') +data = {} +if os.path.exists(path): + with open(path) as f: + data = json.load(f) +data.setdefault('mcpServers', {})['codebase-memory-mcp'] = json.loads('$mcp_entry') +with open(path, 'w') as f: + json.dump(data, f, indent=2) +print() +" + ok "Updated ${settings_file}" + else + warn "Neither jq nor python3 found — cannot auto-configure." + echo "" + info "Add this to ${settings_file} manually:" + echo "" + echo ' "mcpServers": {' + echo ' "codebase-memory-mcp": {' + echo ' "type": "stdio",' + echo " \"command\": \"${binary_path}\"" + echo ' }' + echo ' }' + fi +} + +# --- PATH check --- + +check_path() { + if [[ ":$PATH:" != *":${INSTALL_DIR}:"* ]]; then + echo "" + warn "${INSTALL_DIR} is not on your PATH." + info "Add this to your shell profile (~/.bashrc, ~/.zshrc, etc.):" + echo "" + echo " export PATH=\"${INSTALL_DIR}:\$PATH\"" + fi +} + +# --- Main --- + +echo "" +echo "${BOLD}codebase-memory-mcp installer${RESET}" +echo "" + +if [ "$FROM_SOURCE" = true ]; then + build_from_source +else + platform=$(detect_platform) + ok "Platform: ${platform}" + tool=$(check_download_tool) + ok "Download tool: ${tool}" + download_binary "$platform" "$tool" +fi + +# Verify binary +if [ ! -x "${INSTALL_DIR}/${BINARY_NAME}" ]; then + die "Binary at ${INSTALL_DIR}/${BINARY_NAME} is not executable" +fi + +ver_output=$("${INSTALL_DIR}/${BINARY_NAME}" --version 2>&1) || true +if [ -n "$ver_output" ]; then + ok "$ver_output" +else + ok "Binary is executable" +fi + +configure_claude +check_path + +echo "" +ok "Done! Restart Claude Code and verify with /mcp" +echo "" +info "To uninstall:" +info " rm ${INSTALL_DIR}/${BINARY_NAME}" +info " rm -rf ${SOURCE_DIR} # if built from source" +info " rm -rf ~/.cache/codebase-memory-mcp/ # graph database"