mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-04 10:48:20 +00:00
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)
This commit is contained in:
1 parent
fa444b0ce7
commit
3c6ad62ed0
3 files changed
+773
No files matched your search
@@ -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
|
||||
@@ -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"
|
||||
}
|
||||
Executable
+324
@@ -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 <<JSONEOF
|
||||
{"type":"stdio","command":"${binary_path}"}
|
||||
JSONEOF
|
||||
)
|
||||
|
||||
mkdir -p "$(dirname "$settings_file")"
|
||||
|
||||
if command -v jq &>/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"
|
||||
Reference in new issue
Block a user