Files
bootstrap/install.sh

227 lines
6.1 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
SOURCE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TARGET_DIR="${MOSAIC_HOME:-$HOME/.config/mosaic}"
INSTALL_MODE="${MOSAIC_INSTALL_MODE:-prompt}" # prompt|keep|overwrite
PRESERVE_PATHS=("SOUL.md" "memory")
# Colors (disabled if not a terminal)
if [[ -t 1 ]]; then
GREEN='\033[0;32m' YELLOW='\033[0;33m' RED='\033[0;31m'
CYAN='\033[0;36m' BOLD='\033[1m' RESET='\033[0m'
else
GREEN='' YELLOW='' RED='' CYAN='' BOLD='' RESET=''
fi
ok() { echo -e " ${GREEN}${RESET} $1"; }
warn() { echo -e " ${YELLOW}${RESET} $1" >&2; }
fail() { echo -e " ${RED}${RESET} $1" >&2; }
step() { echo -e "\n${BOLD}$1${RESET}"; }
is_existing_install() {
[[ -d "$TARGET_DIR" ]] || return 1
[[ -f "$TARGET_DIR/bin/mosaic" || -f "$TARGET_DIR/AGENTS.md" || -f "$TARGET_DIR/SOUL.md" ]]
}
select_install_mode() {
case "$INSTALL_MODE" in
keep|overwrite|prompt) ;;
*)
fail "Invalid MOSAIC_INSTALL_MODE='$INSTALL_MODE'. Use: prompt, keep, overwrite."
exit 1
;;
esac
if ! is_existing_install; then
INSTALL_MODE="overwrite"
return
fi
case "$INSTALL_MODE" in
keep|overwrite)
;;
prompt)
if [[ -t 0 ]]; then
echo ""
echo "Existing Mosaic install detected at: $TARGET_DIR"
echo "Choose reinstall mode:"
echo " 1) keep Keep local files (SOUL.md, memory/) while updating framework"
echo " 2) overwrite Replace everything in $TARGET_DIR"
echo " 3) cancel Abort install"
printf "Selection [1/2/3] (default: 1): "
read -r selection
case "${selection:-1}" in
1|k|K|keep|KEEP) INSTALL_MODE="keep" ;;
2|o|O|overwrite|OVERWRITE) INSTALL_MODE="overwrite" ;;
3|c|C|cancel|CANCEL|n|N|no|NO)
fail "Install cancelled."
exit 1
;;
*)
warn "Unrecognized selection '$selection'; defaulting to keep."
INSTALL_MODE="keep"
;;
esac
else
warn "Existing install detected without interactive input; defaulting to keep local files."
INSTALL_MODE="keep"
fi
;;
esac
}
sync_framework() {
local source_real target_real
source_real="$(cd "$SOURCE_DIR" && pwd -P)"
target_real="$(mkdir -p "$TARGET_DIR" && cd "$TARGET_DIR" && pwd -P)"
if [[ "$source_real" == "$target_real" ]]; then
warn "Source and target are the same directory; skipping file sync."
return
fi
if command -v rsync >/dev/null 2>&1; then
local rsync_args=(-a --delete --exclude ".git")
if [[ "$INSTALL_MODE" == "keep" ]]; then
local path
for path in "${PRESERVE_PATHS[@]}"; do
rsync_args+=(--exclude "$path")
done
fi
rsync "${rsync_args[@]}" "$SOURCE_DIR/" "$TARGET_DIR/"
return
fi
local preserve_tmp=""
if [[ "$INSTALL_MODE" == "keep" ]]; then
preserve_tmp="$(mktemp -d "${TMPDIR:-/tmp}/mosaic-preserve-XXXXXX")"
local path
for path in "${PRESERVE_PATHS[@]}"; do
if [[ -e "$TARGET_DIR/$path" ]]; then
mkdir -p "$preserve_tmp/$(dirname "$path")"
cp -R "$TARGET_DIR/$path" "$preserve_tmp/$path"
fi
done
fi
find "$TARGET_DIR" -mindepth 1 -maxdepth 1 ! -name ".git" -exec rm -rf {} +
cp -R "$SOURCE_DIR"/. "$TARGET_DIR"/
rm -rf "$TARGET_DIR/.git"
if [[ -n "$preserve_tmp" ]]; then
local path
for path in "${PRESERVE_PATHS[@]}"; do
if [[ -e "$preserve_tmp/$path" ]]; then
rm -rf "$TARGET_DIR/$path"
mkdir -p "$TARGET_DIR/$(dirname "$path")"
cp -R "$preserve_tmp/$path" "$TARGET_DIR/$path"
fi
done
rm -rf "$preserve_tmp"
fi
}
step "Installing Mosaic framework"
mkdir -p "$TARGET_DIR"
select_install_mode
if [[ "$INSTALL_MODE" == "keep" ]]; then
ok "Install mode: keep local SOUL.md/memory while updating framework"
else
ok "Install mode: overwrite existing files"
fi
sync_framework
chmod +x "$TARGET_DIR"/bin/*
chmod +x "$TARGET_DIR"/install.sh
ok "Framework installed to $TARGET_DIR"
step "Post-install tasks"
if "$TARGET_DIR/bin/mosaic-link-runtime-assets" >/dev/null 2>&1; then
ok "Runtime assets linked"
else
warn "Runtime asset linking failed (non-fatal)"
fi
if [[ "${MOSAIC_SKIP_SKILLS_SYNC:-0}" == "1" ]]; then
ok "Skills sync skipped (MOSAIC_SKIP_SKILLS_SYNC=1)"
else
if "$TARGET_DIR/bin/mosaic-sync-skills" >/dev/null 2>&1; then
ok "Skills synced"
else
warn "Skills sync failed (non-fatal)"
fi
fi
if "$TARGET_DIR/bin/mosaic-migrate-local-skills" --apply >/dev/null 2>&1; then
ok "Local skills migrated"
else
warn "Local skill migration failed (non-fatal)"
fi
if "$TARGET_DIR/bin/mosaic-doctor" >/dev/null 2>&1; then
ok "Health audit passed"
else
warn "Health audit reported issues — run 'mosaic doctor' for details"
fi
step "PATH configuration"
PATH_LINE="export PATH=\"$TARGET_DIR/bin:\$PATH\""
# Find the right shell profile
if [[ -n "${ZSH_VERSION:-}" ]] || [[ "$(basename "${SHELL:-}")" == "zsh" ]]; then
SHELL_PROFILE="$HOME/.zshrc"
elif [[ -f "$HOME/.bashrc" ]]; then
SHELL_PROFILE="$HOME/.bashrc"
elif [[ -f "$HOME/.profile" ]]; then
SHELL_PROFILE="$HOME/.profile"
else
SHELL_PROFILE="$HOME/.profile"
fi
PATH_CHANGED=false
if grep -qF "$TARGET_DIR/bin" "$SHELL_PROFILE" 2>/dev/null; then
ok "Already in PATH via $SHELL_PROFILE"
else
{
echo ""
echo "# Mosaic agent framework"
echo "$PATH_LINE"
} >> "$SHELL_PROFILE"
ok "Added to PATH in $SHELL_PROFILE"
PATH_CHANGED=true
fi
# ── Summary ──────────────────────────────────────────────────
echo ""
echo -e "${GREEN}${BOLD} Mosaic installed successfully.${RESET}"
echo ""
# Collect next steps
NEXT_STEPS=()
if [[ "$PATH_CHANGED" == "true" ]]; then
NEXT_STEPS+=("Run ${CYAN}source $SHELL_PROFILE${RESET} or log out and back in to activate PATH.")
fi
if [[ ! -f "$TARGET_DIR/SOUL.md" ]]; then
NEXT_STEPS+=("Run ${CYAN}mosaic init${RESET} to set up your agent identity (SOUL.md).")
fi
if [[ ${#NEXT_STEPS[@]} -gt 0 ]]; then
echo -e " ${BOLD}Next steps:${RESET}"
for i in "${!NEXT_STEPS[@]}"; do
echo -e " $((i+1)). ${NEXT_STEPS[$i]}"
done
echo ""
fi