From 613b92ce606fb2e786ef1369ee755af60276939a Mon Sep 17 00:00:00 2001 From: kaitranntt Date: Mon, 3 Nov 2025 00:29:41 -0500 Subject: [PATCH] feat(ccs): add --install command for delegation features - Add installation function for commands and skills - Implement --install flag in both bash and PowerShell scripts - Update documentation with installation instructions - Add comprehensive test suites for installation functionality - Refactor delegation command to simplify profile handling - Remove haiku references, focus on GLM delegation model - Update usage examples and documentation --- .claude/commands/ccs.md | 53 +-- .claude/skills/ccs-delegation/SKILL.md | 13 +- .../references/delegation-patterns.md | 9 - README.md | 17 + README.vi.md | 17 + ccs | 93 ++++ ccs.ps1 | 102 ++++ docs/usage.md | 36 ++ tests/install-test.ps1 | 273 +++++++++++ tests/install-test.sh | 446 ++++++++++++++++++ 10 files changed, 1006 insertions(+), 53 deletions(-) create mode 100644 tests/install-test.ps1 create mode 100755 tests/install-test.sh diff --git a/.claude/commands/ccs.md b/.claude/commands/ccs.md index 264b6ea6..1dc810df 100644 --- a/.claude/commands/ccs.md +++ b/.claude/commands/ccs.md @@ -1,13 +1,13 @@ --- allowed-tools: Glob, Read, Bash(jq:*), Task -description: Delegate commands to alternative models (GLM, Haiku) for token optimization +description: Delegate commands to alternative models (GLM) for token optimization argument-hint: [profile] /command [args...] -model: haiku +model: sonnet --- # /ccs - Delegate to Alternative Models -You are a **delegation orchestrator**. Your job is to delegate the user's command to an alternative AI model (GLM, Haiku, etc.) using the CCS (Claude Code Switch) system. +You are a **delegation orchestrator**. Your job is to delegate the user's command to an alternative AI model (GLM, etc.) using the CCS (Claude Code Switch) system. ## User's Input @@ -20,14 +20,14 @@ Follow these steps to delegate the command: ### Step 1: Parse Arguments Extract from the user's input: -- **Profile**: Model profile to use (e.g., `glm`, `haiku`, `son`) - defaults to `glm` if omitted +- **Profile**: Model profile to use (e.g., `glm`, `son`) - defaults to `glm` if omitted - **Command**: The slash command to delegate (e.g., `plan`, `code`, `debug`) - **Arguments**: Additional arguments to pass to the command **Examples**: - `/ccs glm /plan "add auth"` → profile=`glm`, command=`plan`, args=`"add auth"` - `/ccs /code "fix bug"` → profile=`glm` (default), command=`code`, args=`"fix bug"` -- `/ccs haiku /ask "what is X?"` → profile=`haiku`, command=`ask`, args=`"what is X?"` +- `/ccs glm /ask "what is X?"` → profile=`glm`, command=`ask`, args=`"what is X?"` ### Step 2: Validate Profile @@ -48,23 +48,11 @@ Usage: /ccs [profile] /command [args] Example: /ccs glm /plan "add authentication" ``` -### Step 3: Resolve Command Path +### Step 3: Validate Command -Find the command file in this priority order: -1. **User-scope**: `~/.ccs/commands/.md` -2. **Project-scope**: `.claude/commands/.md` +The command will be executed in the launched Claude instance, which will look for `.md` in the project's `.claude/commands/` directory. -Use Glob tool to check both locations. If not found in either: - -``` -❌ Error: Command '/' not found - -Searched locations: - - ~/.ccs/commands/.md (user-scope) - - .claude/commands/.md (project-scope) - -Check command name spelling or create the command file. -``` +**Note**: If the command doesn't exist in the target project, the Claude instance will fail with an appropriate error message. ### Step 4: Launch Subagent with Task Tool @@ -74,7 +62,7 @@ Use the Task tool to delegate execution: ```typescript { subagent_type: "general-purpose", - model: "haiku", + model: "sonnet", description: "Delegating / to profile", prompt: `You are executing a delegated command via CCS (Claude Code Switch). @@ -82,7 +70,6 @@ Use the Task tool to delegate execution: - Profile: - Command: / - Arguments: -- Command file: **CRITICAL**: Before executing, switch to the CCS profile: \`\`\`bash @@ -91,9 +78,8 @@ ccs **Instructions**: 1. Run \`ccs \` to switch to the correct model -2. Read the command file at -3. Execute the command with arguments: -4. Provide a clear summary of what was accomplished +2. Execute the command with arguments: +3. Provide a clear summary of what was accomplished Execute now.` } @@ -159,18 +145,16 @@ ccs --version User: /ccs glm /plan "add user authentication" You: Parse → profile=glm, command=plan, args="add user authentication" Validate → Check glm exists in config ✓ - Resolve → Find ~/.ccs/commands/plan.md ✓ Launch → Task tool with ccs glm Return → Formatted result ``` -**Example 2**: Quick question with Haiku +**Example 2**: Quick question with GLM ``` -User: /ccs haiku /ask "explain JWT tokens" -You: Parse → profile=haiku, command=ask, args="explain JWT tokens" - Validate → Check haiku exists ✓ - Resolve → Find command ✓ - Launch → Delegate to haiku +User: /ccs glm /ask "explain JWT tokens" +You: Parse → profile=glm, command=ask, args="explain JWT tokens" + Validate → Check glm exists ✓ + Launch → Delegate to glm Return → Result ``` @@ -187,11 +171,10 @@ You: Parse → profile=glm (default), command=debug, args="API 500 error" - Each delegation creates an **isolated subagent session** - The subagent switches to the specified CCS profile automatically - **Token optimization** is the primary benefit -- User-scope commands (`~/.ccs/commands/`) are checked first -- Project-scope commands (`.claude/commands/`) are fallback +- Commands are resolved by the launched Claude instance from the project's `.claude/commands/` directory +- If a command doesn't exist, the Claude instance will fail with an error ## Related Documentation -- Setup guide: `tools/ccs/SETUP-DELEGATION.md` - Delegation patterns: `tools/ccs/skills/ccs-delegation.md` - CCS Tool: `tools/ccs/README.md` diff --git a/.claude/skills/ccs-delegation/SKILL.md b/.claude/skills/ccs-delegation/SKILL.md index b2ea1f40..7977f34b 100644 --- a/.claude/skills/ccs-delegation/SKILL.md +++ b/.claude/skills/ccs-delegation/SKILL.md @@ -1,11 +1,11 @@ --- name: ccs-delegation -description: Use this skill when the user invokes the `/ccs` command or requests delegating tasks to alternative models (GLM, Haiku) for token optimization. This skill guides when and how to delegate commands to save primary model tokens. +description: Use this skill when the user invokes the `/ccs` command or requests delegating tasks to alternative models (GLM) for token optimization. This skill guides when and how to delegate commands to save primary model tokens. --- # CCS Delegation -Intelligent task delegation to alternative AI models (GLM, Haiku, etc.) for token optimization using the `/ccs` meta-command. +Intelligent task delegation to alternative AI models (GLM, etc.) for token optimization using the `/ccs` meta-command. ## Purpose @@ -19,7 +19,7 @@ The `/ccs` command delegates simple tasks to alternative models while staying in Load this skill when: - User explicitly invokes `/ccs [profile] /command [args]` - User requests delegating tasks to alternative models -- User asks to use GLM/Haiku for a task +- User asks to use GLM for a task - User requests token conservation strategies ## Decision Framework @@ -125,11 +125,6 @@ Claude: "Delegating to GLM (default profile) for implementation." - Routine fixes - Default choice for simple tasks -**Haiku (haiku profile)**: -- Quick questions -- Simple explanations -- Fast, lightweight tasks -- When speed matters **Sonnet (son profile)**: - Don't delegate—use directly @@ -145,7 +140,7 @@ Claude: "Delegating to GLM (default profile) for implementation." **Examples**: - `/ccs glm /plan "add user authentication"` -- `/ccs haiku /ask "explain JWT tokens"` +- `/ccs glm /ask "explain JWT tokens"` - `/ccs /code "implement feature"` (defaults to glm) ## Error Handling diff --git a/.claude/skills/ccs-delegation/references/delegation-patterns.md b/.claude/skills/ccs-delegation/references/delegation-patterns.md index 7d4fd143..5d76d94c 100644 --- a/.claude/skills/ccs-delegation/references/delegation-patterns.md +++ b/.claude/skills/ccs-delegation/references/delegation-patterns.md @@ -63,15 +63,6 @@ Use GLM for initial implementation, Sonnet for refinement: - ❌ Performance optimization - ❌ Architecture decisions -### Haiku - Best For: -- ✅ Quick questions ("what is X?", "explain Y") -- ✅ Simple explanations -- ✅ Code snippet explanations -- ✅ Quick documentation lookups -- ✅ Simple troubleshooting -- ❌ Code implementation -- ❌ Complex analysis -- ❌ Multi-step tasks ### Sonnet 4.5 - Best For: - ✅ Complex architecture and design diff --git a/README.md b/README.md index a6cd0ef9..514bd492 100644 --- a/README.md +++ b/README.md @@ -53,8 +53,25 @@ irm ccs.kaitran.ca/install | iex ccs # Use Claude subscription (default) ccs glm # Use GLM fallback ccs --version # Show CCS version +ccs --install # Install CCS commands and skills to ~/.claude/ ``` +### Task Delegation + +CCS includes intelligent task delegation via the `/ccs` meta-command: + +```bash +# After running ccs --install, you can use: +/ccs glm /plan "add user authentication" +/ccs glm /code "implement auth endpoints" +/ccs glm /ask "explain this error" +``` + +**Benefits**: +- ✅ Save tokens by delegating simple tasks to cheaper models +- ✅ Use right model for each task automatically +- ✅ Seamless integration with existing workflows + ## Philosophy - **YAGNI**: No features "just in case" diff --git a/README.vi.md b/README.vi.md index f88995ca..226b14db 100644 --- a/README.vi.md +++ b/README.vi.md @@ -53,8 +53,25 @@ irm ccs.kaitran.ca/install | iex ccs # Dùng Claude subscription (mặc định) ccs glm # Dùng GLM fallback ccs --version # Hiển thị phiên bản CCS +ccs --install # Cài đặt lệnh và kỹ năng CCS vào ~/.claude/ ``` +### Delegation Tác Vụ + +CCS bao gồm delegation tác vụ thông minh qua meta-command `/ccs`: + +```bash +# Sau khi chạy ccs --install, bạn có thể dùng: +/ccs glm /plan "add user authentication" +/ccs glm /code "implement auth endpoints" +/ccs glm /ask "explain this error" +``` + +**Lợi ích**: +- ✅ Tiết kiệm tokens bằng cách delegation tác vụ đơn giản cho model rẻ hơn +- ✅ Dùng đúng model cho từng tác vụ tự động +- ✅ Tích hợp liền mạch với workflows hiện có + ## Triết Lý - **YAGNI**: Không có tính năng "phòng hờ" diff --git a/ccs b/ccs index 1f8be1c9..2577efea 100755 --- a/ccs +++ b/ccs @@ -7,6 +7,93 @@ CCS_VERSION="$(cat "$SCRIPT_DIR/VERSION" 2>/dev/null || echo "unknown")" CONFIG_FILE="${CCS_CONFIG:-$HOME/.ccs/config.json}" +# Installation function for commands and skills +install_commands_and_skills() { + local source_dir="$SCRIPT_DIR/.claude" + local target_dir="$HOME/.claude" + + echo "┌─ Installing CCS Commands & Skills" + echo "│ Source: $source_dir" + echo "│ Target: $target_dir" + echo "│" + + # Check if source directory exists + if [[ ! -d "$source_dir" ]]; then + echo "│" + echo "✗ Error: Source directory not found: $source_dir" + echo " Make sure you're running this from the CCS repository directory." + return 1 + fi + + # Create target directories if they don't exist + mkdir -p "$target_dir/commands" + mkdir -p "$target_dir/skills" + + local installed_count=0 + local skipped_count=0 + + # Install commands + if [[ -d "$source_dir/commands" ]]; then + echo "│ Installing commands..." + for cmd_file in "$source_dir/commands"/*.md; do + if [[ -f "$cmd_file" ]]; then + local cmd_name=$(basename "$cmd_file" .md) + local target_file="$target_dir/commands/$cmd_name.md" + + if [[ -f "$target_file" ]]; then + echo "│ │ ℹ Skipping existing command: $cmd_name.md" + skipped_count=$((skipped_count + 1)) + else + if cp "$cmd_file" "$target_file"; then + echo "│ │ ✓ Installed command: $cmd_name.md" + installed_count=$((installed_count + 1)) + else + echo "│ │ ✗ Failed to install command: $cmd_name.md" + fi + fi + fi + done + else + echo "│ ℹ No commands directory found" + fi + + echo "│" + + # Install skills + if [[ -d "$source_dir/skills" ]]; then + echo "│ Installing skills..." + for skill_dir in "$source_dir/skills"/*; do + if [[ -d "$skill_dir" ]]; then + local skill_name=$(basename "$skill_dir") + local target_skill_dir="$target_dir/skills/$skill_name" + + if [[ -d "$target_skill_dir" ]]; then + echo "│ │ ℹ Skipping existing skill: $skill_name" + skipped_count=$((skipped_count + 1)) + else + if cp -r "$skill_dir" "$target_skill_dir"; then + echo "│ │ ✓ Installed skill: $skill_name" + installed_count=$((installed_count + 1)) + else + echo "│ │ ✗ Failed to install skill: $skill_name" + fi + fi + fi + done + else + echo "│ ℹ No skills directory found" + fi + + echo "└─" + echo "" + echo "✓ Installation complete!" + echo " Installed: $installed_count items" + echo " Skipped: $skipped_count items (already exist)" + echo "" + echo "You can now use the /ccs command in Claude CLI for task delegation." + echo "Example: /ccs glm /plan 'add user authentication'" +} + # Special case: version command (check BEFORE profile detection) if [[ $# -gt 0 ]] && [[ "${1}" == "version" || "${1}" == "--version" || "${1}" == "-v" ]]; then echo "CCS (Claude Code Switch) version $CCS_VERSION" @@ -20,6 +107,12 @@ if [[ $# -gt 0 ]] && [[ "${1}" == "--help" || "${1}" == "-h" || "${1}" == "help" exec claude --help "$@" fi +# Special case: install command (check BEFORE profile detection) +if [[ $# -gt 0 ]] && [[ "${1}" == "--install" ]]; then + install_commands_and_skills + exit $? +fi + # Smart profile detection: if first arg starts with '-', it's a flag not a profile if [[ $# -eq 0 ]] || [[ "${1}" =~ ^- ]]; then # No args or first arg is a flag → use default profile diff --git a/ccs.ps1 b/ccs.ps1 index c9736cc4..bb152018 100644 --- a/ccs.ps1 +++ b/ccs.ps1 @@ -21,6 +21,102 @@ $CCS_VERSION = if (Test-Path $VersionFile) { "unknown" } +# Installation function for commands and skills +function Install-CommandsAndSkills { + $SourceDir = Join-Path $ScriptDir ".claude" + $TargetDir = Join-Path $env:USERPROFILE ".claude" + + Write-Host "[Installing CCS Commands & Skills]" -ForegroundColor Cyan + Write-Host "| Source: $SourceDir" + Write-Host "| Target: $TargetDir" + Write-Host "|" + + # Check if source directory exists + if (-not (Test-Path $SourceDir)) { + Write-Host "|" + Write-Host "[ERROR] Source directory not found: $SourceDir" -ForegroundColor Red + Write-Host " Make sure you're running this from the CCS repository directory." + exit 1 + } + + # Create target directories if they don't exist + $CommandsDir = Join-Path $TargetDir "commands" + $SkillsDir = Join-Path $TargetDir "skills" + + if (-not (Test-Path $CommandsDir)) { + New-Item -ItemType Directory -Path $CommandsDir -Force | Out-Null + } + if (-not (Test-Path $SkillsDir)) { + New-Item -ItemType Directory -Path $SkillsDir -Force | Out-Null + } + + $InstalledCount = 0 + $SkippedCount = 0 + + # Install commands + $SourceCommandsDir = Join-Path $SourceDir "commands" + if (Test-Path $SourceCommandsDir) { + Write-Host "| Installing commands..." -ForegroundColor Yellow + Get-ChildItem $SourceCommandsDir -Filter "*.md" | ForEach-Object { + $CmdName = $_.BaseName + $TargetFile = Join-Path $CommandsDir "$CmdName.md" + + if (Test-Path $TargetFile) { + Write-Host "| | [i] Skipping existing command: $CmdName.md" -ForegroundColor Yellow + $SkippedCount++ + } else { + try { + Copy-Item $_.FullName $TargetFile -ErrorAction Stop + Write-Host "| | [OK] Installed command: $CmdName.md" -ForegroundColor Green + $InstalledCount++ + } catch { + Write-Host "| | [!] Failed to install command: $CmdName.md" -ForegroundColor Red + Write-Host "| Error: $($_.Exception.Message)" -ForegroundColor Red + } + } + } + } else { + Write-Host "| [i] No commands directory found" -ForegroundColor Gray + } + + Write-Host "|" + + # Install skills + $SourceSkillsDir = Join-Path $SourceDir "skills" + if (Test-Path $SourceSkillsDir) { + Write-Host "| Installing skills..." -ForegroundColor Yellow + Get-ChildItem $SourceSkillsDir -Directory | ForEach-Object { + $SkillName = $_.Name + $TargetSkillDir = Join-Path $SkillsDir $SkillName + + if (Test-Path $TargetSkillDir) { + Write-Host "| | [i] Skipping existing skill: $SkillName" -ForegroundColor Yellow + $SkippedCount++ + } else { + try { + Copy-Item $_.FullName $TargetSkillDir -Recurse -ErrorAction Stop + Write-Host "| | [OK] Installed skill: $SkillName" -ForegroundColor Green + $InstalledCount++ + } catch { + Write-Host "| | [!] Failed to install skill: $SkillName" -ForegroundColor Red + Write-Host "| Error: $($_.Exception.Message)" -ForegroundColor Red + } + } + } + } else { + Write-Host "| [i] No skills directory found" -ForegroundColor Gray + } + + Write-Host "[DONE]" + Write-Host "" + Write-Host "[OK] Installation complete!" -ForegroundColor Green + Write-Host " Installed: $InstalledCount items" + Write-Host " Skipped: $SkippedCount items (already exist)" + Write-Host "" + Write-Host "You can now use the /ccs command in Claude CLI for task delegation." -ForegroundColor Cyan + Write-Host "Example: /ccs glm /plan 'add user authentication'" -ForegroundColor Cyan +} + # Special case: version command (check BEFORE profile detection) # Check both $ProfileOrFlag and first element of $RemainingArgs $FirstArg = if ($ProfileOrFlag -ne "default") { $ProfileOrFlag } elseif ($RemainingArgs.Count -gt 0) { $RemainingArgs[0] } else { $null } @@ -46,6 +142,12 @@ if ($FirstArg -eq "--help" -or $FirstArg -eq "-h" -or $FirstArg -eq "help") { } } +# Special case: install command (check BEFORE profile detection) +if ($FirstArg -eq "--install") { + Install-CommandsAndSkills + exit $LASTEXITCODE +} + # Smart profile detection: if first arg starts with '-', it's a flag not a profile if ($ProfileOrFlag -match '^-') { # First arg is a flag → use default profile, keep all args diff --git a/docs/usage.md b/docs/usage.md index a2ad73a2..cdfd8199 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -77,6 +77,42 @@ ccs glm /code "implement feature" ```bash ccs --version # Show CCS version ccs --help # Show Claude CLI help +ccs --install # Install CCS commands and skills to ~/.claude/ +``` + +### Installing Commands and Skills + +To use the task delegation feature, you need to install the CCS commands and skills to your Claude CLI directory: + +```bash +# Install CCS delegation commands and skills +ccs --install +``` + +This will: +- Copy `/ccs` command to `~/.claude/commands/ccs.md` +- Copy `ccs-delegation` skill to `~/.claude/skills/ccs-delegation/` +- Skip existing files (won't overwrite your customizations) + +**Output Example**: +``` +┌─ Installing CCS Commands & Skills +│ Source: /path/to/ccs/.claude +│ Target: /home/user/.claude +│ +│ Installing commands... +│ │ ✓ Installed command: ccs.md +│ +│ Installing skills... +│ │ ✓ Installed skill: ccs-delegation +└─ + +✓ Installation complete! + Installed: 2 items + Skipped: 0 items (already exist) + +You can now use the /ccs command in Claude CLI for task delegation. +Example: /ccs glm /plan 'add user authentication' ``` ## Task Delegation diff --git a/tests/install-test.ps1 b/tests/install-test.ps1 new file mode 100644 index 00000000..a3afea37 --- /dev/null +++ b/tests/install-test.ps1 @@ -0,0 +1,273 @@ +#!/usr/bin/env pwsh + +<# +.SYNOPSIS + Test suite for CCS --install functionality (Windows PowerShell) + +.DESCRIPTION + Tests the --install flag for CCS executable on Windows PowerShell. + Tests against actual system installation for simplicity. + +.NOTES + Author: CCS Test Suite + Version: 1.0 + Based on: edge-cases.ps1 patterns +#> + +#Requires -Version 5.1 + +# Don't exit on errors, we're testing +$ErrorActionPreference = "Continue" + +# Test statistics +$Script:TotalTests = 0 +$Script:PassedTests = 0 +$Script:FailedTests = 0 + +# Colors +$Colors = @{ + Red = "Red" + Green = "Green" + Yellow = "Yellow" + Cyan = "Cyan" + White = "White" +} + +function Write-ColorOutput { + param([string]$Message, [string]$Color = "White") + Write-Host $Message -ForegroundColor $Colors[$Color] +} + +function Test-Case { + param( + [string]$Name, + [string]$Description, + [scriptblock]$TestCode + ) + + $Script:TotalTests++ + Write-Host "" + Write-ColorOutput "[$Script:TotalTests] $Name" "Cyan" + Write-ColorOutput " Expected: $Description" "White" + + try { + if (& $TestCode) { + Write-ColorOutput " Result: PASS" "Green" + $Script:PassedTests++ + return $true + } else { + Write-ColorOutput " Result: FAIL" "Red" + $Script:FailedTests++ + return $false + } + } + catch { + Write-ColorOutput " Result: FAIL" "Red" + Write-ColorOutput " Error: $($_.Exception.Message)" "Red" + $Script:FailedTests++ + return $false + } +} + +Write-ColorOutput "========================================" "Yellow" +Write-ColorOutput "CCS --INSTALL FUNCTIONALITY TESTING" "Yellow" +Write-ColorOutput "========================================" "Yellow" +Write-Host "" + +# Get script paths +$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +$CcsRoot = Split-Path -Parent $ScriptDir +$CcsPath = Join-Path $CcsRoot "ccs.ps1" + +# Backup existing .claude directory +Write-ColorOutput "Safety: Backing up existing ~/.claude/..." "Cyan" +$BackupDir = Join-Path $env:TEMP "ccs-test-backup-$(Get-Date -Format 'yyyyMMddHHmmss')" +if (Test-Path $env:USERPROFILE\.claude) { + Write-Host " Found existing ~/.claude/, creating backup at $BackupDir" + Copy-Item -Path $env:USERPROFILE\.claude -Destination $BackupDir -Recurse -Force + Write-ColorOutput " Backup created successfully" "Green" +} else { + Write-ColorOutput " No existing ~/.claude/ found" "Yellow" +} +Write-Host "" + +# ============================================================================ +# PRE-CHECKS +# ============================================================================ +Write-ColorOutput "===== PRE-CHECKS =====" "Yellow" + +Test-Case "CCS executable exists" "ccs.ps1 script found" { + Test-Path $CcsPath +} + +Test-Case "Source .claude directory exists" ".claude/ in CCS repo" { + Test-Path (Join-Path $CcsRoot ".claude") +} + +Test-Case "Source commands exist" "ccs.md command file exists" { + Test-Path (Join-Path $CcsRoot ".claude\commands\ccs.md") +} + +Test-Case "Source skills exist" "ccs-delegation skill directory exists" { + Test-Path (Join-Path $CcsRoot ".claude\skills\ccs-delegation") +} + +# ============================================================================ +# SECTION 1: INSTALLATION +# ============================================================================ +Write-Host "" +Write-ColorOutput "===== SECTION 1: INSTALLATION =====" "Yellow" + +Test-Case "Install: Command executes without error" "Exit code 0" { + $process = Start-Process -FilePath "pwsh" -ArgumentList "-File", "`"$CcsPath`"", "--install" -Wait -PassThru -NoNewWindow + $process.ExitCode -eq 0 +} + +Test-Case "Install: Commands directory created" "~/.claude/commands/ exists" { + Test-Path "$env:USERPROFILE\.claude\commands" +} + +Test-Case "Install: Skills directory created" "~/.claude/skills/ exists" { + Test-Path "$env:USERPROFILE\.claude\skills" +} + +Test-Case "Install: ccs.md command installed" "ccs.md file exists" { + Test-Path "$env:USERPROFILE\.claude\commands\ccs.md" +} + +Test-Case "Install: ccs-delegation skill installed" "ccs-delegation/ exists" { + Test-Path "$env:USERPROFILE\.claude\skills\ccs-delegation" +} + +Test-Case "Install: SKILL.md exists" "SKILL.md in ccs-delegation/" { + Test-Path "$env:USERPROFILE\.claude\skills\ccs-delegation\SKILL.md" +} + +Test-Case "Install: references directory exists" "references/ subdirectory present" { + Test-Path "$env:USERPROFILE\.claude\skills\ccs-delegation\references" +} + +# ============================================================================ +# SECTION 2: IDEMPOTENCY +# ============================================================================ +Write-Host "" +Write-ColorOutput "===== SECTION 2: IDEMPOTENCY =====" "Yellow" + +Test-Case "Idempotent: Second run succeeds" "Second --install doesn't error" { + $process = Start-Process -FilePath "pwsh" -ArgumentList "-File", "`"$CcsPath`"", "--install" -Wait -PassThru -NoNewWindow + $process.ExitCode -eq 0 +} + +# ============================================================================ +# SECTION 3: FILE INTEGRITY +# ============================================================================ +Write-Host "" +Write-ColorOutput "===== SECTION 3: FILE INTEGRITY =====" "Yellow" + +Test-Case "Integrity: Command file not empty" "ccs.md has content" { + $content = Get-Content "$env:USERPROFILE\.claude\commands\ccs.md" -Raw + $content.Length -gt 0 +} + +Test-Case "Integrity: Skill file not empty" "SKILL.md has content" { + $content = Get-Content "$env:USERPROFILE\.claude\skills\ccs-delegation\SKILL.md" -Raw + $content.Length -gt 0 +} + +Test-Case "Integrity: Command file matches source" "ccs.md content identical" { + $source = Get-Content (Join-Path $CcsRoot ".claude\commands\ccs.md") -Raw + $target = Get-Content "$env:USERPROFILE\.claude\commands\ccs.md" -Raw + $source -eq $target +} + +Test-Case "Integrity: Skill file matches source" "SKILL.md content identical" { + $source = Get-Content (Join-Path $CcsRoot ".claude\skills\ccs-delegation\SKILL.md") -Raw + $target = Get-Content "$env:USERPROFILE\.claude\skills\ccs-delegation\SKILL.md" -Raw + $source -eq $target +} + +# ============================================================================ +# SECTION 4: INTEGRATION +# ============================================================================ +Write-Host "" +Write-ColorOutput "===== SECTION 4: INTEGRATION WITH CCS =====" "Yellow" + +Test-Case "Integration: --install executes without profile error" "No profile error on --install" { + $process = Start-Process -FilePath "pwsh" -ArgumentList "-File", "`"$CcsPath`"", "--install" -Wait -PassThru -NoNewWindow + $process.ExitCode -eq 0 +} + +Test-Case "Integration: --version still works" "Version command exits successfully" { + $process = Start-Process -FilePath "pwsh" -ArgumentList "-File", "`"$CcsPath`"", "--version" -Wait -PassThru -NoNewWindow + $process.ExitCode -eq 0 +} + +Test-Case "Integration: --help still works" "Help command exits successfully" { + $process = Start-Process -FilePath "pwsh" -ArgumentList "-File", "`"$CcsPath`"", "--help" -Wait -PassThru -NoNewWindow + $process.ExitCode -eq 0 +} + +# ============================================================================ +# FINAL RESULTS +# ============================================================================ +Write-Host "" +Write-ColorOutput "========================================" "Yellow" +Write-ColorOutput "TEST RESULTS SUMMARY" "Yellow" +Write-ColorOutput "========================================" "Yellow" +Write-Host "" + +Write-ColorOutput "Total Tests: $Script:TotalTests" "Cyan" +Write-ColorOutput "Passed: $Script:PassedTests" "Green" +if ($Script:FailedTests -eq 0) { + Write-ColorOutput "Failed: $Script:FailedTests" "Green" +} else { + Write-ColorOutput "Failed: $Script:FailedTests" "Red" +} +Write-Host "" + +$SuccessRate = if ($Script:TotalTests -gt 0) { + [math]::Round(($Script:PassedTests / $Script:TotalTests) * 100, 2) +} else { 0 } + +if ($SuccessRate -ge 90) { + Write-ColorOutput "Success Rate: $SuccessRate%" "Green" +} elseif ($SuccessRate -ge 70) { + Write-ColorOutput "Success Rate: $SuccessRate%" "Yellow" +} else { + Write-ColorOutput "Success Rate: $SuccessRate%" "Red" +} + +Write-Host "" + +if ($Script:FailedTests -eq 0) { + Write-ColorOutput "========================================" "Green" + Write-ColorOutput "ALL TESTS PASSED!" "Green" + Write-ColorOutput "========================================" "Green" + Write-ColorOutput "--install functionality is production ready!" "Green" +} else { + Write-ColorOutput "========================================" "Red" + Write-ColorOutput "SOME TESTS FAILED" "Red" + Write-ColorOutput "========================================" "Red" + Write-ColorOutput "Review failed tests above for details" "Red" +} + +# Restore backup +Write-Host "" +Write-ColorOutput "Cleaning up and restoring..." "Yellow" +if (Test-Path $env:USERPROFILE\.claude) { + Write-Host " Removing test data from $env:USERPROFILE\.claude" + Remove-Item -Path $env:USERPROFILE\.claude -Recurse -Force +} + +if (Test-Path $BackupDir) { + Write-Host " Restoring backup from $BackupDir" + Move-Item -Path $BackupDir -Destination $env:USERPROFILE\.claude + Write-ColorOutput " Backup restored successfully" "Green" +} + +# Exit with appropriate code +if ($Script:FailedTests -gt 0) { + exit 1 +} else { + exit 0 +} \ No newline at end of file diff --git a/tests/install-test.sh b/tests/install-test.sh new file mode 100755 index 00000000..e34cb887 --- /dev/null +++ b/tests/install-test.sh @@ -0,0 +1,446 @@ +#!/usr/bin/env bash +# CCS --install Functionality Testing (Linux/macOS) +# Comprehensive tests for the --install command + +set +e # Don't exit on errors, we're testing +PASS_COUNT=0 +FAIL_COUNT=0 +TOTAL_TESTS=0 + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +CYAN='\033[0;36m' +GRAY='\033[0;37m' +NC='\033[0m' # No Color + +# Get script directory +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +CCS_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +CCS_PATH="$CCS_ROOT/ccs" + +# Backup/restore paths +CLAUDE_DIR="$HOME/.claude" +BACKUP_DIR="/tmp/ccs-test-backup-$(date +%s)" +TEST_HOME="/tmp/ccs-test-home" +TEST_CLAUDE_DIR="$TEST_HOME/.claude" + +test_case() { + local name="$1" + local expected="$2" + shift 2 + + ((TOTAL_TESTS++)) + echo "" + echo -e "${CYAN}[$TOTAL_TESTS] $name${NC}" + echo -e "${GRAY} Expected: $expected${NC}" + + if "$@"; then + echo -e "${GREEN} Result: PASS${NC}" + ((PASS_COUNT++)) + return 0 + else + echo -e "${RED} Result: FAIL${NC}" + ((FAIL_COUNT++)) + return 1 + fi +} + +cleanup_and_restore() { + echo "" + echo -e "${YELLOW}Cleaning up and restoring...${NC}" + + # Remove test directory + rm -rf "$TEST_HOME" + + # Restore backup if it exists + if [[ -d "$BACKUP_DIR" ]]; then + if [[ -d "$CLAUDE_DIR" ]]; then + echo " Removing test data from $CLAUDE_DIR" + rm -rf "$CLAUDE_DIR" + fi + echo " Restoring backup from $BACKUP_DIR" + mv "$BACKUP_DIR" "$CLAUDE_DIR" + echo -e "${GREEN} Backup restored successfully${NC}" + fi +} + +# Trap to ensure cleanup happens even if script fails +trap cleanup_and_restore EXIT + +echo -e "${YELLOW}========================================${NC}" +echo -e "${YELLOW}CCS --INSTALL FUNCTIONALITY TESTING${NC}" +echo -e "${YELLOW}========================================${NC}" +echo "" + +# ============================================================================ +# SAFETY: BACKUP EXISTING ~/.claude/ +# ============================================================================ +echo -e "${CYAN}Safety: Backing up existing ~/.claude/...${NC}" + +if [[ -d "$CLAUDE_DIR" ]]; then + echo " Found existing ~/.claude/, creating backup at $BACKUP_DIR" + cp -r "$CLAUDE_DIR" "$BACKUP_DIR" + echo -e "${GREEN} Backup created successfully${NC}" +else + echo " No existing ~/.claude/ found, no backup needed" +fi + +echo "" + +# ============================================================================ +# PRE-CHECKS +# ============================================================================ +echo -e "${YELLOW}===== PRE-CHECKS =====${NC}" + +test_case "CCS executable exists" "ccs script found at $CCS_PATH" bash -c " + [[ -f '$CCS_PATH' ]] +" + +test_case "CCS executable is executable" "ccs has execute permissions" bash -c " + [[ -x '$CCS_PATH' ]] +" + +test_case "Source .claude directory exists" ".claude/ in CCS repo" bash -c " + [[ -d '$CCS_ROOT/.claude' ]] +" + +test_case "Source commands exist" "ccs.md command file exists" bash -c " + [[ -f '$CCS_ROOT/.claude/commands/ccs.md' ]] +" + +test_case "Source skills exist" "ccs-delegation skill directory exists" bash -c " + [[ -d '$CCS_ROOT/.claude/skills/ccs-delegation' ]] +" + +# ============================================================================ +# SECTION 1: FRESH INSTALLATION (NO ~/.claude/) +# ============================================================================ +echo "" +echo -e "${YELLOW}===== SECTION 1: FRESH INSTALLATION =====${NC}" + +# Clean any existing test directory +rm -rf "$TEST_HOME" +mkdir -p "$TEST_HOME" + +test_case "Fresh install: Command executes without error" "Exit code 0" bash -c " + HOME='$TEST_HOME' '$CCS_PATH' --install > /tmp/ccs-install-output.txt 2>&1 +" + +test_case "Fresh install: Commands directory created" "~/.claude/commands/ exists" bash -c " + [[ -d '$TEST_CLAUDE_DIR/commands' ]] +" + +test_case "Fresh install: Skills directory created" "~/.claude/skills/ exists" bash -c " + [[ -d '$TEST_CLAUDE_DIR/skills' ]] +" + +test_case "Fresh install: ccs.md command installed" "ccs.md file exists in commands/" bash -c " + [[ -f '$TEST_CLAUDE_DIR/commands/ccs.md' ]] +" + +test_case "Fresh install: ccs-delegation skill installed" "ccs-delegation/ exists in skills/" bash -c " + [[ -d '$TEST_CLAUDE_DIR/skills/ccs-delegation' ]] +" + +test_case "Fresh install: SKILL.md exists" "SKILL.md in ccs-delegation/" bash -c " + [[ -f '$TEST_CLAUDE_DIR/skills/ccs-delegation/SKILL.md' ]] +" + +test_case "Fresh install: references directory exists" "references/ subdirectory present" bash -c " + [[ -d '$TEST_CLAUDE_DIR/skills/ccs-delegation/references' ]] +" + +test_case "Fresh install: delegation-patterns.md exists" "Pattern reference file present" bash -c " + [[ -f '$TEST_CLAUDE_DIR/skills/ccs-delegation/references/delegation-patterns.md' ]] +" + +# ============================================================================ +# SECTION 2: OUTPUT FORMATTING (BOX-DRAWING CHARACTERS) +# ============================================================================ +echo "" +echo -e "${YELLOW}===== SECTION 2: OUTPUT FORMATTING =====${NC}" + +# Clean for fresh test +rm -rf "$TEST_HOME" +mkdir -p "$TEST_HOME" + +test_case "Output: Contains box-drawing header" "Output has ┌─" bash -c " + output=\$(HOME='$TEST_HOME' '$CCS_PATH' --install 2>&1) + [[ \$output =~ ┌─ ]] +" + +test_case "Output: Contains box-drawing footer" "Output has └─" bash -c " + output=\$(HOME='$TEST_HOME' '$CCS_PATH' --install 2>&1) + [[ \$output =~ └─ ]] +" + +test_case "Output: Contains pipe for indentation" "Output has │" bash -c " + output=\$(HOME='$TEST_HOME' '$CCS_PATH' --install 2>&1) + [[ \$output =~ │ ]] +" + +test_case "Output: Contains checkmark indicator" "Output has ✓" bash -c " + output=\$(HOME='$TEST_HOME' '$CCS_PATH' --install 2>&1) + [[ \$output =~ ✓ ]] +" + +test_case "Output: Shows source directory" "Output includes source path" bash -c " + output=\$(HOME='$TEST_HOME' '$CCS_PATH' --install 2>&1) + [[ \$output =~ Source: ]] +" + +test_case "Output: Shows target directory" "Output includes target path" bash -c " + output=\$(HOME='$TEST_HOME' '$CCS_PATH' --install 2>&1) + [[ \$output =~ Target: ]] +" + +test_case "Output: Shows installation complete message" "Success message present" bash -c " + output=\$(HOME='$TEST_HOME' '$CCS_PATH' --install 2>&1) + [[ \$output =~ 'Installation complete' ]] +" + +test_case "Output: Shows installed count" "Displays number of installed items" bash -c " + output=\$(HOME='$TEST_HOME' '$CCS_PATH' --install 2>&1) + [[ \$output =~ 'Installed:' ]] +" + +test_case "Output: NO emoji characters used" "Output uses only box-drawing and symbols" bash -c " + # Check for actual emoji characters (not box-drawing or symbols like ✓ ℹ ✗) + output=\$(HOME='$TEST_HOME' '$CCS_PATH' --install 2>&1) + ! [[ \$output =~ [🚀📋🎯🎉] ]] +" + +# ============================================================================ +# SECTION 3: IDEMPOTENCY (MULTIPLE RUNS) +# ============================================================================ +echo "" +echo -e "${YELLOW}===== SECTION 3: IDEMPOTENCY =====${NC}" + +# Start fresh +rm -rf "$TEST_HOME" +mkdir -p "$TEST_HOME" +HOME="$TEST_HOME" "$CCS_PATH" --install > /dev/null 2>&1 + +test_case "Idempotent: Second run succeeds" "Second --install doesn't error" bash -c " + HOME='$TEST_HOME' '$CCS_PATH' --install > /tmp/ccs-install-second.txt 2>&1 +" + +test_case "Idempotent: Shows skip messages" "Output contains 'Skipping existing'" bash -c " + output=\$(HOME='$TEST_HOME' '$CCS_PATH' --install 2>&1) + [[ \$output =~ 'Skipping existing' ]] +" + +test_case "Idempotent: Uses info indicator ℹ" "Skip messages have ℹ indicator" bash -c " + output=\$(HOME='$TEST_HOME' '$CCS_PATH' --install 2>&1) + [[ \$output =~ ℹ ]] +" + +test_case "Idempotent: Skipped count shown" "Displays number of skipped items" bash -c " + output=\$(HOME='$TEST_HOME' '$CCS_PATH' --install 2>&1) + [[ \$output =~ 'Skipped:' ]] +" + +test_case "Idempotent: Files remain unchanged" "Original files not modified" bash -c " + # Get modification time before second install + mtime1=\$(stat -c %Y '$TEST_CLAUDE_DIR/commands/ccs.md' 2>/dev/null || stat -f %m '$TEST_CLAUDE_DIR/commands/ccs.md') + sleep 1 + HOME='$TEST_HOME' '$CCS_PATH' --install > /dev/null 2>&1 + mtime2=\$(stat -c %Y '$TEST_CLAUDE_DIR/commands/ccs.md' 2>/dev/null || stat -f %m '$TEST_CLAUDE_DIR/commands/ccs.md') + [[ \$mtime1 == \$mtime2 ]] +" + +test_case "Idempotent: Third run still safe" "Multiple runs are safe" bash -c " + HOME='$TEST_HOME' '$CCS_PATH' --install > /dev/null 2>&1 + HOME='$TEST_HOME' '$CCS_PATH' --install > /dev/null 2>&1 + [[ -f '$TEST_CLAUDE_DIR/commands/ccs.md' ]] && [[ -d '$TEST_CLAUDE_DIR/skills/ccs-delegation' ]] +" + +# ============================================================================ +# SECTION 4: CONFLICT HANDLING (EXISTING FILES) +# ============================================================================ +echo "" +echo -e "${YELLOW}===== SECTION 4: CONFLICT HANDLING =====${NC}" + +# Create existing files with different content +rm -rf "$TEST_CLAUDE_DIR" +mkdir -p "$TEST_CLAUDE_DIR/commands" +mkdir -p "$TEST_CLAUDE_DIR/skills/ccs-delegation" +echo "EXISTING COMMAND CONTENT" > "$TEST_CLAUDE_DIR/commands/ccs.md" +echo "EXISTING SKILL CONTENT" > "$TEST_CLAUDE_DIR/skills/ccs-delegation/SKILL.md" + +test_case "Conflict: Skips existing command file" "Doesn't overwrite ccs.md" bash -c " + HOME='$TEST_HOME' '$CCS_PATH' --install > /dev/null 2>&1 + content=\$(cat '$TEST_CLAUDE_DIR/commands/ccs.md') + [[ \$content == 'EXISTING COMMAND CONTENT' ]] +" + +test_case "Conflict: Skips existing skill directory" "Doesn't overwrite skill" bash -c " + content=\$(cat '$TEST_CLAUDE_DIR/skills/ccs-delegation/SKILL.md') + [[ \$content == 'EXISTING SKILL CONTENT' ]] +" + +test_case "Conflict: Output shows skip message" "User informed about skips" bash -c " + output=\$(HOME='$TEST_HOME' '$CCS_PATH' --install 2>&1) + [[ \$output =~ 'Skipping existing command: ccs.md' ]] +" + +test_case "Conflict: No data loss" "Original files preserved" bash -c " + [[ -f '$TEST_CLAUDE_DIR/commands/ccs.md' ]] && + grep -q 'EXISTING COMMAND CONTENT' '$TEST_CLAUDE_DIR/commands/ccs.md' +" + +# ============================================================================ +# SECTION 5: FILE INTEGRITY +# ============================================================================ +echo "" +echo -e "${YELLOW}===== SECTION 5: FILE INTEGRITY =====${NC}" + +# Fresh install for integrity checks +rm -rf "$TEST_HOME" +mkdir -p "$TEST_HOME" +HOME="$TEST_HOME" "$CCS_PATH" --install > /dev/null 2>&1 + +test_case "Integrity: Command file not empty" "ccs.md has content" bash -c " + [[ -s '$TEST_CLAUDE_DIR/commands/ccs.md' ]] +" + +test_case "Integrity: Skill file not empty" "SKILL.md has content" bash -c " + [[ -s '$TEST_CLAUDE_DIR/skills/ccs-delegation/SKILL.md' ]] +" + +test_case "Integrity: Command file matches source" "ccs.md content identical" bash -c " + diff -q '$CCS_ROOT/.claude/commands/ccs.md' '$TEST_CLAUDE_DIR/commands/ccs.md' +" + +test_case "Integrity: Skill file matches source" "SKILL.md content identical" bash -c " + diff -q '$CCS_ROOT/.claude/skills/ccs-delegation/SKILL.md' '$TEST_CLAUDE_DIR/skills/ccs-delegation/SKILL.md' +" + +test_case "Integrity: Reference file matches source" "delegation-patterns.md identical" bash -c " + diff -q '$CCS_ROOT/.claude/skills/ccs-delegation/references/delegation-patterns.md' \ + '$TEST_CLAUDE_DIR/skills/ccs-delegation/references/delegation-patterns.md' +" + +test_case "Integrity: Directory structure preserved" "All subdirectories present" bash -c " + [[ -d '$TEST_CLAUDE_DIR/skills/ccs-delegation/references' ]] +" + +# ============================================================================ +# SECTION 6: ERROR HANDLING +# ============================================================================ +echo "" +echo -e "${YELLOW}===== SECTION 6: ERROR HANDLING =====${NC}" + +test_case "Error: Missing source directory handled" "Graceful error when source missing" bash -c " + # Temporarily move .claude directory + mv '$CCS_ROOT/.claude' '$CCS_ROOT/.claude.backup' + output=\$(HOME='$TEST_HOME' '$CCS_PATH' --install 2>&1 || true) + mv '$CCS_ROOT/.claude.backup' '$CCS_ROOT/.claude' + [[ \$output =~ 'Error: Source directory not found' ]] +" + +test_case "Error: Shows helpful error message" "Error message explains the issue" bash -c " + mv '$CCS_ROOT/.claude' '$CCS_ROOT/.claude.backup' + output=\$(HOME='$TEST_HOME' '$CCS_PATH' --install 2>&1 || true) + mv '$CCS_ROOT/.claude.backup' '$CCS_ROOT/.claude' + [[ \$output =~ 'CCS repository directory' ]] +" + +test_case "Error: Uses error indicator ✗" "Error messages have ✗ indicator" bash -c " + mv '$CCS_ROOT/.claude' '$CCS_ROOT/.claude.backup' + output=\$(HOME='$TEST_HOME' '$CCS_PATH' --install 2>&1 || true) + mv '$CCS_ROOT/.claude.backup' '$CCS_ROOT/.claude' + [[ \$output =~ ✗ ]] +" + +# ============================================================================ +# SECTION 7: PERMISSIONS AND OWNERSHIP +# ============================================================================ +echo "" +echo -e "${YELLOW}===== SECTION 7: PERMISSIONS AND OWNERSHIP =====${NC}" + +rm -rf "$TEST_CLAUDE_DIR" +HOME=$(dirname "$TEST_CLAUDE_DIR") "$CCS_PATH" --install > /dev/null 2>&1 + +test_case "Permissions: Command file is readable" "ccs.md has read permissions" bash -c " + [[ -r '$TEST_CLAUDE_DIR/commands/ccs.md' ]] +" + +test_case "Permissions: Skill file is readable" "SKILL.md has read permissions" bash -c " + [[ -r '$TEST_CLAUDE_DIR/skills/ccs-delegation/SKILL.md' ]] +" + +test_case "Permissions: Directories are accessible" "Can list directory contents" bash -c " + ls '$TEST_CLAUDE_DIR/commands' > /dev/null 2>&1 && + ls '$TEST_CLAUDE_DIR/skills' > /dev/null 2>&1 +" + +# ============================================================================ +# SECTION 8: INTEGRATION WITH CCS +# ============================================================================ +echo "" +echo -e "${YELLOW}===== SECTION 8: INTEGRATION WITH CCS =====${NC}" + +test_case "Integration: --install flag recognized" "--install doesn't show 'Profile not found'" bash -c " + output=\$('$CCS_PATH' --install 2>&1 || true) + ! [[ \$output =~ 'Profile.*--install.*not found' ]] +" + +test_case "Integration: --version still works after --install" "Version command functional" bash -c " + output=\$('$CCS_PATH' --version 2>&1) + [[ \$output =~ 'CCS' ]] +" + +test_case "Integration: --help still works after --install" "Help command functional" bash -c " + output=\$('$CCS_PATH' --help 2>&1) + [[ \$output =~ 'Usage' ]] || [[ \$output =~ 'Claude' ]] +" + +# ============================================================================ +# FINAL RESULTS +# ============================================================================ +echo "" +echo -e "${YELLOW}========================================${NC}" +echo -e "${YELLOW}TEST RESULTS SUMMARY${NC}" +echo -e "${YELLOW}========================================${NC}" +echo "" +echo -e "${CYAN}Total Tests: $TOTAL_TESTS${NC}" +echo -e "${GREEN}Passed: $PASS_COUNT${NC}" + +if [[ $FAIL_COUNT -eq 0 ]]; then + echo -e "${GREEN}Failed: $FAIL_COUNT${NC}" +else + echo -e "${RED}Failed: $FAIL_COUNT${NC}" +fi + +echo "" + +SUCCESS_RATE=$(awk "BEGIN {printf \"%.2f\", ($PASS_COUNT / $TOTAL_TESTS) * 100}") + +if awk "BEGIN {exit !($SUCCESS_RATE >= 90)}"; then + echo -e "${GREEN}Success Rate: $SUCCESS_RATE%${NC}" +elif awk "BEGIN {exit !($SUCCESS_RATE >= 70)}"; then + echo -e "${YELLOW}Success Rate: $SUCCESS_RATE%${NC}" +else + echo -e "${RED}Success Rate: $SUCCESS_RATE%${NC}" +fi + +echo "" + +if [[ $FAIL_COUNT -eq 0 ]]; then + echo -e "${GREEN}========================================${NC}" + echo -e "${GREEN}ALL TESTS PASSED!${NC}" + echo -e "${GREEN}========================================${NC}" + echo "" + echo -e "${GREEN}--install functionality is production ready!${NC}" + exit 0 +else + echo -e "${YELLOW}========================================${NC}" + echo -e "${YELLOW}SOME TESTS FAILED${NC}" + echo -e "${YELLOW}========================================${NC}" + echo "" + echo -e "${YELLOW}Review failed tests above for details${NC}" + exit 1 +fi