#!/usr/bin/env bash # install.sh — install mempalace-toolkit executables + companion agent skill # # Idempotent. Safe to re-run after container recreate. set -euo pipefail # ── locate self ────────────────────────────────────── SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" # ── targets ────────────────────────────────────────── BIN_SRC="${SCRIPT_DIR}/bin" BIN_DEST="${HOME}/.local/bin" SKILL_SRC="${SCRIPT_DIR}/SKILL.md" SKILL_DEST_DIR="${HOME}/.agents/skills/opencode-mempalace-bridge" SKILL_DEST="${SKILL_DEST_DIR}/SKILL.md" # ── args ───────────────────────────────────────────── ACTION="install" ASSUME_YES="no" while [[ $# -gt 0 ]]; do case "$1" in --uninstall) ACTION="uninstall"; shift ;; -y|--yes) ASSUME_YES="yes"; shift ;; -h|--help) cat <&2; exit 2 ;; esac done # ── helpers ────────────────────────────────────────── ok() { printf ' \e[32m✓\e[0m %s\n' "$*"; } note() { printf '==> %s\n' "$*"; } warn() { printf ' \e[33m!\e[0m %s\n' "$*" >&2; } err() { printf ' \e[31m✗\e[0m %s\n' "$*" >&2; } confirm() { [[ "$ASSUME_YES" == "yes" ]] && return 0 read -r -p "Proceed? [y/N] " ans [[ "$ans" =~ ^[Yy]$ ]] } link_if_into_repo() { # Return 0 if $1 is a symlink pointing into $SCRIPT_DIR local target [[ -L "$1" ]] || return 1 target=$(readlink -f "$1") [[ "$target" == "$SCRIPT_DIR"/* ]] } # ── install ────────────────────────────────────────── install_bin() { mkdir -p "$BIN_DEST" note "Symlinking bin/ executables into $BIN_DEST" local count=0 for src in "$BIN_SRC"/*; do [[ -x "$src" && -f "$src" ]] || continue local name; name=$(basename "$src") local dest="$BIN_DEST/$name" if [[ -e "$dest" || -L "$dest" ]]; then if link_if_into_repo "$dest"; then ok "Already linked: $name" count=$((count+1)) continue else warn "Skipping $name: $dest exists and is not our symlink" continue fi fi ln -s "$src" "$dest" ok "Linked $name → $src" count=$((count+1)) done echo ok "Installed $count executable(s)" } install_skill() { note "Linking companion agent skill" mkdir -p "$SKILL_DEST_DIR" if [[ -e "$SKILL_DEST" || -L "$SKILL_DEST" ]]; then if link_if_into_repo "$SKILL_DEST"; then ok "Skill already linked" return 0 else warn "Skipping skill: $SKILL_DEST exists and is not our symlink" return 0 fi fi ln -s "$SKILL_SRC" "$SKILL_DEST" ok "Linked SKILL.md → $SKILL_SRC" } check_path() { case ":$PATH:" in *":$BIN_DEST:"*) : ;; *) warn "$BIN_DEST is not on \$PATH. Add to your shell rc:"; printf ' export PATH="%s:$PATH"\n' "\$HOME/.local/bin" ;; esac } do_install() { echo echo "mempalace-toolkit installer" echo "Repository: $SCRIPT_DIR" echo echo "==> Installation plan:" echo " Symlink executables in bin/ into $BIN_DEST" echo " Symlink SKILL.md into $SKILL_DEST" echo confirm || { echo "Aborted."; exit 0; } echo install_bin echo install_skill echo check_path echo ok "Done." echo echo "Next: ./bin/mempalace-session --dry-run" echo " or: ./bin/mempalace-docs /path/to/project --dry-run" } # ── uninstall ──────────────────────────────────────── do_uninstall() { echo echo "mempalace-toolkit uninstaller" echo "Repository: $SCRIPT_DIR" echo confirm || { echo "Aborted."; exit 0; } echo note "Removing executable symlinks from $BIN_DEST" local removed=0 for src in "$BIN_SRC"/*; do [[ -x "$src" && -f "$src" ]] || continue local name; name=$(basename "$src") local dest="$BIN_DEST/$name" if link_if_into_repo "$dest"; then rm "$dest" ok "Removed $name" removed=$((removed+1)) fi done ok "Removed $removed executable symlink(s)" echo note "Removing skill symlink" if link_if_into_repo "$SKILL_DEST"; then rm "$SKILL_DEST" ok "Removed skill symlink" else ok "No skill symlink to remove" fi echo ok "Done." } case "$ACTION" in install) do_install ;; uninstall) do_uninstall ;; esac