From 92cfc5a07925fa2290cb2084c0acfbef7432e864 Mon Sep 17 00:00:00 2001 From: Digvijay Chauhan Date: Wed, 25 Feb 2026 16:26:23 +0100 Subject: [PATCH] feat(scripts): add uninstall scripts for Linux/Mac and Windows Adds scripts/uninstall.sh and scripts/uninstall.ps1 that cleanly remove spec2cloud from a project. Both scripts follow the same style and conventions as the existing install scripts. Features: - Detects and lists what will be removed before proceeding - Interactive confirmation prompt (skippable with --force) - Removes agents, prompts, MCP config, devcontainer, apm.yml, AGENTS.md, and .spec2cloud backup files - Cleans up empty directories (.github, .vscode, .devcontainer) - Preserves specs/ by default (opt-in removal with --remove-specs) - Colored output with --no-color option - Usage help with --help Also updates INTEGRATION.md to document the new uninstall scripts, replacing the previous manual rm commands. --- INTEGRATION.md | 39 +++-- scripts/uninstall.ps1 | 328 ++++++++++++++++++++++++++++++++++++++ scripts/uninstall.sh | 357 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 711 insertions(+), 13 deletions(-) create mode 100644 scripts/uninstall.ps1 create mode 100644 scripts/uninstall.sh diff --git a/INTEGRATION.md b/INTEGRATION.md index 71ff1c1..4400d23 100644 --- a/INTEGRATION.md +++ b/INTEGRATION.md @@ -402,26 +402,39 @@ curl -fsSL https://raw.githubusercontent.com/EmeaAppGbb/spec2cloud/main/scripts/ ## 🗑️ Uninstalling -To remove spec2cloud: +Use the provided uninstall scripts to cleanly remove spec2cloud: +**Linux/Mac**: ```bash -# Remove agents and prompts -rm -rf .github/agents -rm -rf .github/prompts +# Remove agents, prompts, and configs (preserves specs/) +./scripts/uninstall.sh -# Remove specs directory (be careful - may contain your work!) -# Only if you want to remove generated documentation -rm -rf specs/ +# Remove everything including generated specs +./scripts/uninstall.sh --remove-specs -# Remove configurations (if no conflicts) -rm .vscode/mcp.json -rm .devcontainer/devcontainer.json -rm apm.yml +# Skip confirmation prompts +./scripts/uninstall.sh --force +``` + +**Windows**: +```powershell +# Remove agents, prompts, and configs (preserves specs/) +.\scripts\uninstall.ps1 -# Remove any .spec2cloud backup files -find . -name "*.spec2cloud" -delete +# Remove everything including generated specs +.\scripts\uninstall.ps1 -RemoveSpecs + +# Skip confirmation prompts +.\scripts\uninstall.ps1 -Force ``` +The uninstall scripts will: +- Remove `.github/agents/` and `.github/prompts/` +- Remove `.vscode/mcp.json`, `.devcontainer/devcontainer.json`, `apm.yml`, `AGENTS.md` +- Remove any `*.spec2cloud` backup files +- Clean up empty directories +- Optionally remove `specs/` (only with `--remove-specs` / `-RemoveSpecs`) + ## 💡 Best Practices ### 1. Start Small diff --git a/scripts/uninstall.ps1 b/scripts/uninstall.ps1 new file mode 100644 index 0000000..a097c76 --- /dev/null +++ b/scripts/uninstall.ps1 @@ -0,0 +1,328 @@ +# Spec2Cloud Uninstall Script (PowerShell) +# Removes only spec2cloud-installed agents, prompts, and configurations from a project. +# Custom agents/prompts added by the user are preserved. + +param( + [Parameter(Position=0)] + [string]$TargetDir = ".", + + [switch]$RemoveSpecs, + [switch]$Force, + [switch]$NoColor, + [switch]$Help +) + +$VERSION = "1.0.0" +$ErrorActionPreference = "Stop" + +# Known spec2cloud agent filenames (installed by install.ps1) +$Spec2CloudAgents = @( + "architect.agent.md" + "azure.agent.md" + "dev.agent.md" + "devlead.agent.md" + "extender.agent.md" + "modernizer.agent.md" + "planner.agent.md" + "pm.agent.md" + "spec2cloud.agent.md" + "tech-analyst.agent.md" +) + +# Known spec2cloud prompt filenames (installed by install.ps1) +$Spec2CloudPrompts = @( + "adr.prompt.md" + "bootstrap-agents.prompt.md" + "delegate.prompt.md" + "deploy.prompt.md" + "extend.prompt.md" + "frd.prompt.md" + "generate-agents.prompt.md" + "implement.prompt.md" + "modernize.prompt.md" + "plan.prompt.md" + "prd.prompt.md" + "rev-eng.prompt.md" +) + +# Color support +if (-not $NoColor -and $Host.UI.SupportsVirtualTerminal) { + $RED = "`e[31m" + $GREEN = "`e[32m" + $YELLOW = "`e[33m" + $BLUE = "`e[34m" + $BOLD = "`e[1m" + $NC = "`e[0m" +} else { + $RED = $GREEN = $YELLOW = $BLUE = $BOLD = $NC = "" +} + +function Write-Header { + Write-Host "${BLUE}${BOLD}" -NoNewline + Write-Host "╔═══════════════════════════════════════════════════════════╗" + Write-Host "║ Spec2Cloud Uninstaller ║" + Write-Host "║ AI-Powered Development Workflows ║" + Write-Host "╚═══════════════════════════════════════════════════════════╝" + Write-Host "${NC}" +} + +function Write-Usage { + @" +Usage: uninstall.ps1 [OPTIONS] [TARGET_DIR] + +Remove spec2cloud from an existing project. + +OPTIONS: + -RemoveSpecs Also remove specs/ directory (contains generated docs!) + -Force Skip confirmation prompts + -NoColor Disable colored output + -Help Show this help message + +TARGET_DIR: + Directory to uninstall from (default: current directory) + +EXAMPLES: + # Remove agents, prompts, and configs (preserve specs/) + .\uninstall.ps1 + + # Remove everything including generated specs + .\uninstall.ps1 -RemoveSpecs + + # Force removal without prompts + .\uninstall.ps1 -Force + +"@ +} + +function Log-Info { + param([string]$Message) + Write-Host "${BLUE}ℹ${NC} $Message" +} + +function Log-Success { + param([string]$Message) + Write-Host "${GREEN}✓${NC} $Message" +} + +function Log-Warning { + param([string]$Message) + Write-Host "${YELLOW}⚠${NC} $Message" +} + +function Log-Error { + param([string]$Message) + Write-Host "${RED}✗${NC} $Message" +} + +# Show help +if ($Help) { + Write-Usage + exit 0 +} + +# Convert to absolute path +$resolved = Resolve-Path $TargetDir -ErrorAction SilentlyContinue +if (-not $resolved) { + Log-Error "Target directory does not exist: $TargetDir" + exit 1 +} +$TargetDir = $resolved.Path + +function Test-Installation { + $agentsExist = Test-Path "$TargetDir\.github\agents" + $promptsExist = Test-Path "$TargetDir\.github\prompts" + + if (-not $agentsExist -and -not $promptsExist) { + Log-Error "No spec2cloud installation found in $TargetDir" + exit 1 + } +} + +function Confirm-Removal { + if ($Force) { return } + + Write-Host "" + Log-Warning "This will remove the following spec2cloud components from $TargetDir :" + Write-Host "" + + # Count spec2cloud agent files that exist + $agentCount = 0 + foreach ($agent in $Spec2CloudAgents) { + if (Test-Path (Join-Path "$TargetDir\.github\agents" $agent)) { + $agentCount++ + } + } + if ($agentCount -gt 0) { + Write-Host " - $agentCount spec2cloud agent(s) from .github\agents\" + } + + # Count spec2cloud prompt files that exist + $promptCount = 0 + foreach ($prompt in $Spec2CloudPrompts) { + if (Test-Path (Join-Path "$TargetDir\.github\prompts" $prompt)) { + $promptCount++ + } + } + if ($promptCount -gt 0) { + Write-Host " - $promptCount spec2cloud prompt(s) from .github\prompts\" + } + + # Detect custom files that will be PRESERVED + $customAgents = 0 + $customPrompts = 0 + if (Test-Path "$TargetDir\.github\agents") { + Get-ChildItem "$TargetDir\.github\agents" -Filter "*.agent.md" -ErrorAction SilentlyContinue | ForEach-Object { + if ($_.Name -notin $Spec2CloudAgents) { $customAgents++ } + } + } + if (Test-Path "$TargetDir\.github\prompts") { + Get-ChildItem "$TargetDir\.github\prompts" -Filter "*.prompt.md" -ErrorAction SilentlyContinue | ForEach-Object { + if ($_.Name -notin $Spec2CloudPrompts) { $customPrompts++ } + } + } + if ($customAgents -gt 0 -or $customPrompts -gt 0) { + Write-Host "" + Log-Info "Preserving $customAgents custom agent(s) and $customPrompts custom prompt(s)" + } + + if (Test-Path "$TargetDir\.vscode\mcp.json") { + Write-Host " - .vscode\mcp.json (MCP server config)" + } + if (Test-Path "$TargetDir\.devcontainer\devcontainer.json") { + Write-Host " - .devcontainer\devcontainer.json (dev container config)" + } + if (Test-Path "$TargetDir\apm.yml") { + Write-Host " - apm.yml (APM configuration)" + } + + $backups = Get-ChildItem -Path $TargetDir -Filter "*.spec2cloud" -Recurse -ErrorAction SilentlyContinue + if ($backups) { + Write-Host " - *.spec2cloud backup files ($($backups.Count) files)" + } + + if ($RemoveSpecs) { + Write-Host "" + Log-Warning "Also removing specs\ directory (generated documentation)!" + Write-Host " - specs\ (PRDs, FRDs, tasks, docs)" + } + + Write-Host "" + $response = Read-Host "Continue? (y/N)" + if ($response -notmatch "^[Yy]$") { + Log-Info "Uninstall cancelled." + exit 0 + } +} + +function Remove-AgentsAndPrompts { + # Remove only known spec2cloud agents + $removed = 0 + foreach ($agent in $Spec2CloudAgents) { + $path = Join-Path "$TargetDir\.github\agents" $agent + if (Test-Path $path) { + Remove-Item $path -Force + $removed++ + } + } + if ($removed -gt 0) { + Log-Success "Removed $removed spec2cloud agent(s)" + } + + # Remove only known spec2cloud prompts + $removed = 0 + foreach ($prompt in $Spec2CloudPrompts) { + $path = Join-Path "$TargetDir\.github\prompts" $prompt + if (Test-Path $path) { + Remove-Item $path -Force + $removed++ + } + } + if ($removed -gt 0) { + Log-Success "Removed $removed spec2cloud prompt(s)" + } + + # Clean up empty directories (only if nothing custom remains) + foreach ($dir in @(".github\prompts", ".github\agents", ".github")) { + $fullPath = Join-Path $TargetDir $dir + if ((Test-Path $fullPath) -and (Get-ChildItem $fullPath | Measure-Object).Count -eq 0) { + Remove-Item $fullPath -Force + Log-Success "Removed empty $dir\" + } + } +} + +function Remove-Configs { + # MCP config + if (Test-Path "$TargetDir\.vscode\mcp.json") { + Remove-Item "$TargetDir\.vscode\mcp.json" -Force + Log-Success "Removed .vscode\mcp.json" + } + + # Dev container + if (Test-Path "$TargetDir\.devcontainer\devcontainer.json") { + Remove-Item "$TargetDir\.devcontainer\devcontainer.json" -Force + Log-Success "Removed .devcontainer\devcontainer.json" + } + + # APM config + if (Test-Path "$TargetDir\apm.yml") { + Remove-Item "$TargetDir\apm.yml" -Force + Log-Success "Removed apm.yml" + } + + # AGENTS.md (auto-generated by apm compile) + if (Test-Path "$TargetDir\AGENTS.md") { + Remove-Item "$TargetDir\AGENTS.md" -Force + Log-Success "Removed AGENTS.md" + } + + # Remove .spec2cloud backup files + $backups = Get-ChildItem -Path $TargetDir -Filter "*.spec2cloud" -Recurse -ErrorAction SilentlyContinue + if ($backups) { + $backups | Remove-Item -Force + Log-Success "Removed $($backups.Count) .spec2cloud backup files" + } + + # Clean up empty directories + foreach ($dir in @(".vscode", ".devcontainer")) { + $fullPath = Join-Path $TargetDir $dir + if ((Test-Path $fullPath) -and (Get-ChildItem $fullPath | Measure-Object).Count -eq 0) { + Remove-Item $fullPath -Force + Log-Success "Removed empty $dir\" + } + } +} + +function Remove-Specs { + if ($RemoveSpecs -and (Test-Path "$TargetDir\specs")) { + Remove-Item "$TargetDir\specs" -Recurse -Force + Log-Success "Removed specs\" + } +} + +# Main uninstall flow +Write-Header + +Log-Info "Target directory: $TargetDir" +Write-Host "" + +Test-Installation +Confirm-Removal + +Write-Host "" +Log-Info "Removing spec2cloud components..." +Write-Host "" + +Remove-AgentsAndPrompts +Remove-Configs +Remove-Specs + +Write-Host "" +Log-Success "Spec2Cloud has been uninstalled from $TargetDir" +Write-Host "" + +# Check if specs were preserved +if (-not $RemoveSpecs -and (Test-Path "$TargetDir\specs")) { + Log-Info "specs\ directory was preserved. Remove manually if not needed:" + Write-Host " Remove-Item -Recurse -Force $TargetDir\specs" +} diff --git a/scripts/uninstall.sh b/scripts/uninstall.sh new file mode 100644 index 0000000..1f8717a --- /dev/null +++ b/scripts/uninstall.sh @@ -0,0 +1,357 @@ +#!/bin/bash + +# Spec2Cloud Uninstall Script +# Removes only spec2cloud-installed agents, prompts, and configurations from a project +# Custom agents/prompts added by the user are preserved. + +set -e + +VERSION="1.0.0" +COLORS=true + +# Known spec2cloud agent filenames (installed by install.sh) +SPEC2CLOUD_AGENTS=( + architect.agent.md + azure.agent.md + dev.agent.md + devlead.agent.md + extender.agent.md + modernizer.agent.md + planner.agent.md + pm.agent.md + spec2cloud.agent.md + tech-analyst.agent.md +) + +# Known spec2cloud prompt filenames (installed by install.sh) +SPEC2CLOUD_PROMPTS=( + adr.prompt.md + bootstrap-agents.prompt.md + delegate.prompt.md + deploy.prompt.md + extend.prompt.md + frd.prompt.md + generate-agents.prompt.md + implement.prompt.md + modernize.prompt.md + plan.prompt.md + prd.prompt.md + rev-eng.prompt.md +) + +# Color codes +if [ "$COLORS" = true ]; then + RED='\033[0;31m' + GREEN='\033[0;32m' + YELLOW='\033[1;33m' + BLUE='\033[0;34m' + BOLD='\033[1m' + NC='\033[0m' +else + RED='' + GREEN='' + YELLOW='' + BLUE='' + BOLD='' + NC='' +fi + +# Default options +REMOVE_SPECS=false +FORCE=false +TARGET_DIR="." + +print_header() { + echo -e "${BLUE}${BOLD}" + echo "╔═══════════════════════════════════════════════════════════╗" + echo "║ Spec2Cloud Uninstaller ║" + echo "║ AI-Powered Development Workflows ║" + echo "╚═══════════════════════════════════════════════════════════╝" + echo -e "${NC}" +} + +print_usage() { + cat << EOF +Usage: $0 [OPTIONS] [TARGET_DIR] + +Remove spec2cloud from an existing project. + +OPTIONS: + --remove-specs Also remove specs/ directory (contains generated docs!) + --force Skip confirmation prompts + --no-color Disable colored output + --help Show this help message + +TARGET_DIR: + Directory to uninstall from (default: current directory) + +EXAMPLES: + # Remove agents, prompts, and configs (preserve specs/) + $0 + + # Remove everything including generated specs + $0 --remove-specs + + # Force removal without prompts + $0 --force + +EOF +} + +log_info() { + echo -e "${BLUE}ℹ${NC} $1" +} + +log_success() { + echo -e "${GREEN}✓${NC} $1" +} + +log_warning() { + echo -e "${YELLOW}⚠${NC} $1" +} + +log_error() { + echo -e "${RED}✗${NC} $1" +} + +# Parse arguments +while [[ $# -gt 0 ]]; do + case $1 in + --remove-specs) REMOVE_SPECS=true; shift ;; + --force) FORCE=true; shift ;; + --no-color) COLORS=false; RED=''; GREEN=''; YELLOW=''; BLUE=''; BOLD=''; NC=''; shift ;; + --help) print_usage; exit 0 ;; + *) TARGET_DIR="$1"; shift ;; + esac +done + +# Resolve target directory +TARGET_DIR="$(cd "$TARGET_DIR" 2>/dev/null && pwd)" || { + log_error "Target directory does not exist: $TARGET_DIR" + exit 1 +} + +# Verify spec2cloud is installed +check_installation() { + if [ ! -d "$TARGET_DIR/.github/agents" ] && [ ! -d "$TARGET_DIR/.github/prompts" ]; then + log_error "No spec2cloud installation found in $TARGET_DIR" + exit 1 + fi +} + +# Confirm with user +confirm_removal() { + if [ "$FORCE" = true ]; then + return + fi + + echo "" + log_warning "This will remove the following spec2cloud components from $TARGET_DIR:" + echo "" + + # List specific agent files that exist + local agent_count=0 + for agent in "${SPEC2CLOUD_AGENTS[@]}"; do + if [ -f "$TARGET_DIR/.github/agents/$agent" ]; then + agent_count=$((agent_count + 1)) + fi + done + if [ "$agent_count" -gt 0 ]; then + echo " - $agent_count spec2cloud agent(s) from .github/agents/" + fi + + # List specific prompt files that exist + local prompt_count=0 + for prompt in "${SPEC2CLOUD_PROMPTS[@]}"; do + if [ -f "$TARGET_DIR/.github/prompts/$prompt" ]; then + prompt_count=$((prompt_count + 1)) + fi + done + if [ "$prompt_count" -gt 0 ]; then + echo " - $prompt_count spec2cloud prompt(s) from .github/prompts/" + fi + + # Show custom files that will be PRESERVED + local custom_agents=0 + if [ -d "$TARGET_DIR/.github/agents" ]; then + for f in "$TARGET_DIR/.github/agents/"*.agent.md; do + [ -f "$f" ] || continue + local base + base=$(basename "$f") + local is_spec2cloud=false + for known in "${SPEC2CLOUD_AGENTS[@]}"; do + if [ "$base" = "$known" ]; then + is_spec2cloud=true + break + fi + done + if [ "$is_spec2cloud" = false ]; then + custom_agents=$((custom_agents + 1)) + fi + done + fi + + local custom_prompts=0 + if [ -d "$TARGET_DIR/.github/prompts" ]; then + for f in "$TARGET_DIR/.github/prompts/"*.prompt.md; do + [ -f "$f" ] || continue + local base + base=$(basename "$f") + local is_spec2cloud=false + for known in "${SPEC2CLOUD_PROMPTS[@]}"; do + if [ "$base" = "$known" ]; then + is_spec2cloud=true + break + fi + done + if [ "$is_spec2cloud" = false ]; then + custom_prompts=$((custom_prompts + 1)) + fi + done + fi + + if [ "$custom_agents" -gt 0 ] || [ "$custom_prompts" -gt 0 ]; then + echo "" + log_info "Preserving $custom_agents custom agent(s) and $custom_prompts custom prompt(s)" + fi + + [ -f "$TARGET_DIR/.vscode/mcp.json" ] && echo " - .vscode/mcp.json (MCP server config)" + [ -f "$TARGET_DIR/.devcontainer/devcontainer.json" ] && echo " - .devcontainer/devcontainer.json (dev container config)" + [ -f "$TARGET_DIR/apm.yml" ] && echo " - apm.yml (APM configuration)" + + # Show .spec2cloud backup files + spec2cloud_backups=$(find "$TARGET_DIR" -name "*.spec2cloud" 2>/dev/null || true) + if [ -n "$spec2cloud_backups" ]; then + echo " - *.spec2cloud backup files" + fi + + if [ "$REMOVE_SPECS" = true ]; then + echo "" + log_warning "Also removing specs/ directory (generated documentation)!" + echo " - specs/ (PRDs, FRDs, tasks, docs)" + fi + + echo "" + read -p "Continue? (y/N) " response + if [[ ! "$response" =~ ^[Yy]$ ]]; then + log_info "Uninstall cancelled." + exit 0 + fi +} + +# Remove only spec2cloud agents and prompts (preserves custom files) +remove_agents_and_prompts() { + local removed=0 + + # Remove only known spec2cloud agents + for agent in "${SPEC2CLOUD_AGENTS[@]}"; do + if [ -f "$TARGET_DIR/.github/agents/$agent" ]; then + rm "$TARGET_DIR/.github/agents/$agent" + removed=$((removed + 1)) + fi + done + if [ "$removed" -gt 0 ]; then + log_success "Removed $removed spec2cloud agent(s)" + fi + + # Remove only known spec2cloud prompts + removed=0 + for prompt in "${SPEC2CLOUD_PROMPTS[@]}"; do + if [ -f "$TARGET_DIR/.github/prompts/$prompt" ]; then + rm "$TARGET_DIR/.github/prompts/$prompt" + removed=$((removed + 1)) + fi + done + if [ "$removed" -gt 0 ]; then + log_success "Removed $removed spec2cloud prompt(s)" + fi + + # Clean up empty directories (only if nothing custom remains) + for dir in ".github/prompts" ".github/agents" ".github"; do + if [ -d "$TARGET_DIR/$dir" ] && [ -z "$(ls -A "$TARGET_DIR/$dir" 2>/dev/null)" ]; then + rmdir "$TARGET_DIR/$dir" + log_success "Removed empty $dir/" + fi + done +} + +# Remove configuration files +remove_configs() { + # MCP config + if [ -f "$TARGET_DIR/.vscode/mcp.json" ]; then + rm "$TARGET_DIR/.vscode/mcp.json" + log_success "Removed .vscode/mcp.json" + fi + + # Dev container + if [ -f "$TARGET_DIR/.devcontainer/devcontainer.json" ]; then + rm "$TARGET_DIR/.devcontainer/devcontainer.json" + log_success "Removed .devcontainer/devcontainer.json" + fi + + # APM config + if [ -f "$TARGET_DIR/apm.yml" ]; then + rm "$TARGET_DIR/apm.yml" + log_success "Removed apm.yml" + fi + + # AGENTS.md (auto-generated by apm compile) + if [ -f "$TARGET_DIR/AGENTS.md" ]; then + rm "$TARGET_DIR/AGENTS.md" + log_success "Removed AGENTS.md" + fi + + # Remove .spec2cloud backup files + local backup_count=0 + while IFS= read -r -d '' backup; do + rm "$backup" + backup_count=$((backup_count + 1)) + done < <(find "$TARGET_DIR" -name "*.spec2cloud" -print0 2>/dev/null) + + if [ "$backup_count" -gt 0 ]; then + log_success "Removed $backup_count .spec2cloud backup files" + fi + + # Clean up empty directories + for dir in ".vscode" ".devcontainer"; do + if [ -d "$TARGET_DIR/$dir" ] && [ -z "$(ls -A "$TARGET_DIR/$dir" 2>/dev/null)" ]; then + rmdir "$TARGET_DIR/$dir" + log_success "Removed empty $dir/" + fi + done +} + +# Remove specs directory +remove_specs() { + if [ "$REMOVE_SPECS" = true ] && [ -d "$TARGET_DIR/specs" ]; then + rm -rf "$TARGET_DIR/specs" + log_success "Removed specs/" + fi +} + +# Main uninstall flow +print_header + +log_info "Target directory: $TARGET_DIR" +echo "" + +check_installation +confirm_removal + +echo "" +log_info "Removing spec2cloud components..." +echo "" + +remove_agents_and_prompts +remove_configs +remove_specs + +echo "" +log_success "Spec2Cloud has been uninstalled from $TARGET_DIR" +echo "" + +# Check if specs were preserved +if [ "$REMOVE_SPECS" = false ] && [ -d "$TARGET_DIR/specs" ]; then + log_info "specs/ directory was preserved. Remove manually if not needed:" + echo " rm -rf $TARGET_DIR/specs" +fi