Files
multi-agent-mux/deploy/remove.sh
T

319 lines
8.8 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# ==============================================================================
# remove.sh — Multi-Agent Mux (MAM) Orchestration Uninstaller
# ==============================================================================
# Safely removes MAM orchestration skills, virtual environment, and metadata.
# Leaves pre-existing user configurations and files untouched by reading
# the installation manifest (.mam/install_manifest.txt).
# ==============================================================================
set -euo pipefail
TARGET_DIR=""
FORCE=0
PURGE_ENV=0
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Parse arguments
while [[ $# -gt 0 ]]; do
case "$1" in
-y|--yes|--force)
FORCE=1
shift
;;
--purge-env)
PURGE_ENV=1
shift
;;
*)
TARGET_DIR="$1"
shift
;;
esac
done
if [ -z "$TARGET_DIR" ]; then
if [ "$(basename "$SCRIPT_DIR")" = ".mam_deploy" ]; then
TARGET_DIR="$(dirname "$SCRIPT_DIR")"
else
TARGET_DIR="$(pwd)"
fi
else
if [ "$(basename "$TARGET_DIR")" = ".mam_deploy" ]; then
TARGET_DIR="$(dirname "$TARGET_DIR")"
fi
fi
echo "===================================================================="
echo "⚡ Starting Multi-Agent Mux (MAM) Uninstallation"
echo "📂 Target Workspace: $TARGET_DIR"
echo "===================================================================="
if [ ! -d "$TARGET_DIR" ]; then
echo "❌ Error: Target directory '$TARGET_DIR' does not exist." >&2
exit 1
fi
cd "$TARGET_DIR"
GI_CREATED=0
if [ -f ".mam/install_state" ]; then
GI_CREATED=$(grep '^gitignore_created=' .mam/install_state 2>/dev/null | cut -d= -f2 || echo 0)
fi
# 1. Non-interactive input safety guard (set -e read crash prevention)
if [ ! -t 0 ] && [ $FORCE -eq 0 ]; then
echo "❌ Error: Non-interactive terminal detected. Please run with -y/--yes/--force." >&2
exit 1
fi
# Check if there is anything to remove
MANIFEST_FILE=".mam/install_manifest.txt"
any_exist=0
manifest_files=()
# Load the install manifest if it exists
if [ -f "$MANIFEST_FILE" ]; then
any_exist=1
while IFS= read -r line; do
if [ -n "$line" ]; then
manifest_files+=("$line")
fi
done < "$MANIFEST_FILE"
else
fallback_assets=(
".agents/skills/lib.sh"
".agents/skills/multi-agent-mux-create"
".agents/skills/multi-agent-mux-delegate-job"
".agents/skills/multi-agent-mux-loop"
".agents/skills/multi-agent-mux-monitor"
".agents/skills/multi-agent-mux-resume"
".agents/skills/multi-agent-mux-status"
".agents/skills/multi-agent-mux-stop"
".agents/hooks"
".agents/hooks.json"
".agents/MULTI_AGENT_RULES.md"
".agents/MULTI_AGENT_RULES.ko.md"
".agents/INSTALL.md"
".venv"
".mam"
".mam_deploy"
)
for asset in "${fallback_assets[@]}"; do
if [ -e "$asset" ] || [ -h "$asset" ]; then
any_exist=1
break
fi
done
fi
if [ $any_exist -eq 0 ]; then
echo "️ No MAM assets detected in '$TARGET_DIR'. Nothing to do."
exit 0
fi
# Request confirmation if not forced
if [ $FORCE -eq 0 ]; then
echo "⚠️ WARNING: This will permanently remove the MAM orchestration skills, "
echo " virtual environment (.venv), local metadata (.mam), and docs."
if ! read -p "❓ Are you sure you want to proceed? [y/N]: " -r response; then
response="n"
fi
if [[ ! "$response" =~ ^[yY](es)?$ ]]; then
echo "❌ Uninstallation cancelled by user."
exit 0
fi
fi
delete_asset() {
local asset="$1"
if [ -e "$asset" ] || [ -h "$asset" ]; then
echo "🗑️ Removing: $asset"
rm -rf "$asset"
fi
}
# Check for modified skills before deleting
TS=$(date -u +%Y%m%dT%H%M%SZ)
python3 - ".mam/asset_hashes.txt" "$TS" <<'PY' 2>/dev/null || true
import sys, os, hashlib, shutil
hash_db_path = sys.argv[1]
ts = sys.argv[2]
if not os.path.exists(hash_db_path):
sys.exit(0)
modified = []
with open(hash_db_path, "r") as f:
for line in f:
parts = line.strip().split(None, 1)
if len(parts) == 2:
expected_hash, path = parts[0], parts[1]
if os.path.exists(path):
h = hashlib.sha256()
with open(path, "rb") as pf:
while chunk := pf.read(65536):
h.update(chunk)
if h.hexdigest() != expected_hash:
modified.append(path)
if modified:
backup_dir = f".mam-skill-backup.{ts}"
os.makedirs(backup_dir, exist_ok=True)
for p in modified:
dest = os.path.join(backup_dir, p)
os.makedirs(os.path.dirname(dest), exist_ok=True)
shutil.copy2(p, dest)
print(f"💾 Preserved {len(modified)} modified skill file(s) under {backup_dir}")
PY
# 2. Uninstall files using the manifest if present
if [ ${#manifest_files[@]} -gt 0 ]; then
echo "📜 Manifest found. Reversing installer-created files..."
for f in ${manifest_files[@]+"${manifest_files[@]}"}; do
if [ "$f" = ".env" ] || [ "$f" = ".mam.env" ] || [ "$f" = "remove.sh" ] || [ "$f" = ".mam_deploy/remove.sh" ]; then
continue
fi
delete_asset "$f"
done
else
echo "⚠️ No manifest found. Deleting standard MAM assets..."
for asset in ${fallback_assets[@]+"${fallback_assets[@]}"}; do
case "$asset" in
.venv|.mam|.mam_deploy) continue ;; # handled explicitly further down
esac
delete_asset "$asset"
done
fi
if [ -d ".agents" ]; then
find .agents -depth -type d -exec rmdir {} + 2>/dev/null || true
fi
# Clean up .gitignore managed block (C8)
MAM_GI_START="# >>> MAM managed block (managed by install.sh — do not edit) >>>"
MAM_GI_END="# <<< MAM managed block <<<"
if [ -f .gitignore ]; then
python3 - .gitignore "$MAM_GI_START" "$MAM_GI_END" "$GI_CREATED" <<'PY'
import sys, os
gi_path, start_marker, end_marker, gi_created = sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4] == "1"
if not os.path.exists(gi_path):
sys.exit(0)
with open(gi_path, "r") as f:
lines = f.readlines()
new_lines = []
in_block = False
block_found = False
for line in lines:
if line.strip() == start_marker:
in_block = True
block_found = True
continue
if line.strip() == end_marker:
in_block = False
continue
if not in_block:
new_lines.append(line)
if block_found:
content = "".join(new_lines).strip()
if gi_created and not content:
os.remove(gi_path)
else:
with open(gi_path, "w") as f:
f.writelines(new_lines)
PY
fi
delete_asset ".venv"
delete_asset ".cache/multi-agent-mux-monitor"
rmdir ".cache" 2>/dev/null || true
delete_asset ".mam"
for env_name in ".mam.env" ".env"; do
[ -f "$env_name" ] || continue
env_created_by_mam=0
for f in ${manifest_files[@]+"${manifest_files[@]}"}; do
if [ "$f" = "$env_name" ]; then
env_created_by_mam=1
break
fi
done
should_delete_env=0
if [ $PURGE_ENV -eq 1 ]; then
should_delete_env=1
elif [ $env_created_by_mam -eq 1 ] && [ $FORCE -eq 0 ]; then
if ! read -p "❓ MAM-created '$env_name' found. Delete it? (Saying No preserves it) [y/N]: " -r env_response; then
env_response="n"
fi
if [[ "$env_response" =~ ^[yY](es)?$ ]]; then
should_delete_env=1
fi
fi
if [ $should_delete_env -eq 1 ]; then
delete_asset "$env_name"
else
if [ $env_created_by_mam -eq 1 ]; then
already_preserved=0
for existing in "${env_name}.mam-backup" "${env_name}".mam-backup.*; do
[ -f "$existing" ] || continue
if cmp -s "$env_name" "$existing" 2>/dev/null; then
already_preserved=1
rm -f "$env_name"
echo "️ '$env_name' is already preserved in $existing (no duplicate created)."
break
fi
done
if [ $already_preserved -eq 0 ]; then
slot="${env_name}.mam-backup"
if [ -e "$slot" ]; then
slot="${env_name}.mam-backup.$(date +%Y%m%d%H%M%S)"
n=1
while [ -e "$slot" ]; do
slot="${env_name}.mam-backup.$(date +%Y%m%d%H%M%S)-$n"
n=$((n + 1))
done
fi
mv "$env_name" "$slot"
echo "💾 Backed up $env_name -> $slot"
fi
else
echo "️ Preserving user-owned $env_name configuration."
fi
fi
done
# Remove uninstaller file(s)
for self in ".mam_deploy/remove.sh" "remove.sh"; do
[ -f "$self" ] || continue
in_manifest=0
for f in ${manifest_files[@]+"${manifest_files[@]}"}; do
if [ "$f" = "$self" ]; then
in_manifest=1
break
fi
done
if [ $in_manifest -eq 1 ] || [ $FORCE -eq 1 ]; then
echo "🗑️ Removing uninstaller: $self"
rm -f "$self"
fi
done
delete_asset ".mam_deploy/update.sh"
rmdir .mam_deploy 2>/dev/null || true
echo "===================================================================="
echo "🎉 Uninstallation complete!"
echo "===================================================================="