diff --git a/scripts/bootstrap.sh b/scripts/bootstrap.sh new file mode 100755 index 000000000..7775f42e6 --- /dev/null +++ b/scripts/bootstrap.sh @@ -0,0 +1,129 @@ +#!/usr/bin/env bash +# MSD Core bootstrap — one-line install from the public repo. +# +# curl -fsSL https://msd.golem15.com | bash +# curl -fsSL https://msd.golem15.com | bash -s -- --codex --profile standard +# +# Clones (or updates) the MSD checkout into $MSD_HOME, builds it, and runs +# bin/install.js globally for the chosen runtime(s). Installs Claude Code +# first when it is the target runtime and `claude` is not on PATH. +# +# Everything lives inside main() so a truncated download never runs half a script. + +main() { + set -euo pipefail + + local repo="${MSD_REPO:-https://git.golem15.com/golem15/msd-core.git}" + local ref="${MSD_REF:-next}" + local home_dir="${MSD_HOME:-$HOME/.local/share/msd-core}" + local profile="${MSD_PROFILE:-full}" + local install_claude=true + local runtimes=() + + while (($#)); do + case "$1" in + --claude|--codex|--opencode|--cursor|--zcode|--antigravity) runtimes+=("$1") ;; + --profile) + shift + (($#)) || { echo "ERROR: --profile requires a value." >&2; exit 2; } + profile="$1" + ;; + --profile=*) profile="${1#--profile=}" ;; + --ref) + shift + (($#)) || { echo "ERROR: --ref requires a value." >&2; exit 2; } + ref="$1" + ;; + --skip-claude-install) install_claude=false ;; + -h|--help) + cat <<'EOF' +MSD Core bootstrap + +Usage: curl -fsSL https://msd.golem15.com | bash -s -- [options] + +Options: + --claude, --codex, --opencode, --cursor, --zcode, --antigravity + Runtime(s) to install for (default: --claude) + --profile NAME Skill profile: core, standard or full (default: full) + --ref REF Branch or tag to install (default: next) + --skip-claude-install Do not install Claude Code when it is missing + +Environment: MSD_HOME (checkout dir, default ~/.local/share/msd-core), + MSD_REPO, MSD_REF, MSD_PROFILE +EOF + exit 0 + ;; + *) echo "ERROR: unknown argument: $1 (try --help)" >&2; exit 2 ;; + esac + shift + done + ((${#runtimes[@]})) || runtimes=(--claude) + + case "$profile" in + core|standard|full) ;; + *) echo "ERROR: profile must be core, standard or full; got: $profile" >&2; exit 2 ;; + esac + + say() { printf '\033[36m==>\033[0m %s\n' "$*"; } + + command -v git >/dev/null 2>&1 || { echo "ERROR: git is required." >&2; exit 1; } + local node_major + node_major=$(node -p 'process.versions.node.split(".")[0]' 2>/dev/null || echo 0) + if ((node_major < 24)); then + cat >&2 </dev/null || echo none)). +Install it, e.g. with nvm: + curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash + nvm install 24 +then rerun this command. +EOF + exit 1 + fi + + if ! command -v jq >/dev/null 2>&1; then + printf '\033[33mWARNING:\033[0m jq is not installed; MSD workflows rely on it.\n' >&2 + echo ' Install it (apt install jq / brew install jq) after this finishes.' >&2 + fi + + if [[ " ${runtimes[*]} " == *" --claude "* ]] && $install_claude \ + && ! command -v claude >/dev/null 2>&1 && [[ ! -x "$HOME/.local/bin/claude" ]]; then + say "Installing Claude Code" + curl -fsSL https://claude.ai/install.sh | bash + fi + + if [[ -d "$home_dir/.git" ]]; then + say "Updating $home_dir ($ref)" + if [[ -n "$(git -C "$home_dir" status --porcelain --untracked-files=no)" ]]; then + echo "ERROR: $home_dir has local changes; commit or discard them first." >&2 + exit 1 + fi + git -C "$home_dir" fetch --quiet origin "$ref" + git -C "$home_dir" checkout --quiet --detach FETCH_HEAD + else + say "Cloning MSD into $home_dir ($ref)" + mkdir -p "$(dirname "$home_dir")" + git clone --quiet --branch "$ref" "$repo" "$home_dir" + fi + + cd "$home_dir" + say "Installing dependencies and building" + npm ci --no-audit --no-fund --loglevel=error + npm run --silent build:hooks + + local rt + for rt in "${runtimes[@]}"; do + say "Installing MSD for ${rt#--} (profile: $profile)" + node bin/install.js "$rt" --global "--profile=$profile" + done + + cat </dev/null 2>&1; then + echo 'Add Claude Code to your PATH: export PATH="$HOME/.local/bin:$PATH"' + fi +} + +main "$@"