#!/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 "$@"