Files
msd-core/scripts/bootstrap.sh
Jakub Zych 6524974d93 feat: install and update msd from git release tags, not npm
MSD is not published to npm, so /msd-update and the SessionStart update
check could never find a release. Releases are now vX.Y.Z git tags plus a
stable branch:

- check-latest-version reads release tags via git ls-remote against the
  baked repository URL (latest = highest stable tag, next = incl. -rc)
- /msd-update reruns the bootstrap installer pinned to the checked tag and
  reads the changelog from that tag
- bootstrap.sh defaults to the stable branch and supports --local/--global
- package identity: changelog URL on stable, manual install via
  msd.golem15.com
- README quickstart and update how-to describe the one-line installer
- track the repo-root CLAUDE.md

Emitted-Drift-Ack-Growth: update.md — the update flow now explains the git-tag release lookup and pins the bootstrap installer to the checked tag, replacing the npm/npx wording
2026-10-09 10:54:04 +02:00

139 lines
4.8 KiB
Bash
Executable File

#!/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 at the `stable` branch
# (the latest tagged release), builds it, and runs bin/install.js for the
# chosen runtime(s) — globally by default, or into the current directory with
# --local. 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:-stable}"
local home_dir="${MSD_HOME:-$HOME/.local/share/msd-core}"
local profile="${MSD_PROFILE:-full}"
local install_claude=true
local scope=--global
local start_dir="$PWD"
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"
;;
--global|--local) scope="$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: stable, the
latest release; e.g. v2.0.0, or next for unreleased work)
--global | --local Install into the runtime's home config (default) or
into the current directory's project config
--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 <<EOF
ERROR: MSD needs Node.js 24 or newer (found: $(node --version 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
cd "$start_dir"
for rt in "${runtimes[@]}"; do
say "Installing MSD for ${rt#--} (${scope#--}, profile: $profile)"
node "$home_dir/bin/install.js" "$rt" "$scope" "--profile=$profile"
done
cat <<EOF
MSD installed from $home_dir ($(git -C "$home_dir" describe --tags --always)).
Update any time by rerunning: curl -fsSL https://msd.golem15.com | bash
EOF
if [[ " ${runtimes[*]} " == *" --claude "* ]] && ! command -v claude >/dev/null 2>&1; then
echo 'Add Claude Code to your PATH: export PATH="$HOME/.local/bin:$PATH"'
fi
}
main "$@"