diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml index 13f3e3c4..7496c949 100644 --- a/.github/workflows/publish-npm.yml +++ b/.github/workflows/publish-npm.yml @@ -49,7 +49,7 @@ jobs: body: | ## Installation ```bash - npm install -g @kai/ccs + npm install -g @kaitranntt/ccs ``` ## What's Changed diff --git a/.gitignore b/.gitignore index f68f2caa..068eedba 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ # Plans directory plans/ +*.tgz \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index bd325334..3bb778cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,42 @@ All notable changes to CCS will be documented here. Format based on [Keep a Changelog](https://keepachangelog.com/). +## [2.4.1] - 2025-11-04 + +### Fixed +- **CRITICAL: PowerShell Terminal Termination**: Fixed PowerShell 7 terminal closing when using `irm | iex` installation + - Changed `exit 1` to `return` in install.ps1 line 229 for piped script contexts + - Terminal now stays open on installation errors, showing error messages properly + - Affects: Windows PowerShell 5.1+, PowerShell 7+, all piped installations +- **Installation Download Path**: Fixed incorrect download path in install.ps1 + - Changed `/ccs.ps1` to `/lib/ccs.ps1` (line 223) to match repository structure + - Resolves standalone installation failures from GitHub +- **Claude CLI Detection**: Simplified detection logic, removed overengineered validation + - Removed complex path validation that failed with npm-installed Claude CLI (.cmd wrappers) + - Now trusts system PATH for Claude detection (standard case for users) + - Falls back to CCS_CLAUDE_PATH if set for custom installations + - Affects: Both bash (lib/ccs) and PowerShell (lib/ccs.ps1) versions + - Fixes: `where.exe claude` shows Claude exists but CCS reports "not found" + +### Changed +- **Error Messages**: Simplified Claude CLI not found error message + - Removed lengthy "searched locations" output + - Focused on actionable solutions (install, verify, set custom path) + - Cleaner UX with less information overload + +### Technical Details +- **Files Modified**: + - `installers/install.ps1`: Line 229 (exit → return), Line 223 (download path fix) + - `lib/ccs.ps1`: Lines 27-72 (simplified detection, removed Test-ClaudeCli function) + - `lib/ccs`: Lines 32-72 (simplified detection, removed validate_claude_cli function) + - `bin/claude-detector.js`: Lines 1-113 (simplified detection for npm package) + - `bin/ccs.js`: Removed validateClaudeCli calls, simplified error handling +- **Root Cause**: `exit` in piped PowerShell scripts terminates entire session, not just script +- **Solution**: `return` exits script scope only, preserving terminal +- **Cross-Platform Parity**: Applied same simplification to bash, PowerShell, and Node.js versions +- **npm Package**: Updated with simplified detection logic (v2.4.1) +- **Testing**: Validated bash version, npm package syntax, manual Windows testing recommended + ## [2.4.0] - 2025-11-04 ### ⚠️ BREAKING CHANGES diff --git a/VERSION b/VERSION index 197c4d5c..005119ba 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.4.0 +2.4.1 diff --git a/bin/ccs.js b/bin/ccs.js index fda6407b..01e34113 100755 --- a/bin/ccs.js +++ b/bin/ccs.js @@ -5,7 +5,7 @@ const { spawn } = require('child_process'); const path = require('path'); const fs = require('fs'); const { showError, colors } = require('./helpers'); -const { detectClaudeCli, validateClaudeCli, showClaudeNotFoundError } = require('./claude-detector'); +const { detectClaudeCli, showClaudeNotFoundError } = require('./claude-detector'); const { getSettingsPath } = require('./config-manager'); // Version (sync with package.json) @@ -26,21 +26,8 @@ function handleVersionCommand() { } function handleHelpCommand(remainingArgs) { - // Detect and validate Claude CLI const claudeCli = detectClaudeCli(); - if (!claudeCli) { - showClaudeNotFoundError(); - process.exit(1); - } - - try { - validateClaudeCli(claudeCli); - } catch (e) { - showError(e.message); - process.exit(1); - } - // Execute claude --help const child = spawn(claudeCli, ['--help', ...remainingArgs], { stdio: 'inherit' }); @@ -53,7 +40,7 @@ function handleHelpCommand(remainingArgs) { }); child.on('error', (err) => { - console.error(`Error executing claude --help: ${err.message}`); + showClaudeNotFoundError(); process.exit(1); }); } @@ -124,18 +111,6 @@ function main() { if (profile === 'default') { const claudeCli = detectClaudeCli(); - if (!claudeCli) { - showClaudeNotFoundError(); - process.exit(1); - } - - try { - validateClaudeCli(claudeCli); - } catch (e) { - showError(e.message); - process.exit(1); - } - // Execute claude with args const child = spawn(claudeCli, remainingArgs, { stdio: 'inherit' }); @@ -148,7 +123,7 @@ function main() { }); child.on('error', (err) => { - console.error(`Error executing claude: ${err.message}`); + showClaudeNotFoundError(); process.exit(1); }); @@ -161,19 +136,6 @@ function main() { // Detect Claude CLI const claudeCli = detectClaudeCli(); - if (!claudeCli) { - showClaudeNotFoundError(); - process.exit(1); - } - - // Validate Claude CLI path - try { - validateClaudeCli(claudeCli); - } catch (e) { - showError(e.message); - process.exit(1); - } - // Execute claude with --settings const claudeArgs = ['--settings', settingsPath, ...remainingArgs]; const child = spawn(claudeCli, claudeArgs, { stdio: 'inherit' }); @@ -187,7 +149,7 @@ function main() { }); child.on('error', (err) => { - console.error(`Error executing claude: ${err.message}`); + showClaudeNotFoundError(); process.exit(1); }); } diff --git a/bin/claude-detector.js b/bin/claude-detector.js index cb0a69b2..78006848 100644 --- a/bin/claude-detector.js +++ b/bin/claude-detector.js @@ -1,156 +1,58 @@ 'use strict'; const fs = require('fs'); -const path = require('path'); -const { execSync } = require('child_process'); -const { showError, expandPath, isPathSafe } = require('./helpers'); +const { showError, expandPath } = require('./helpers'); // Detect Claude CLI executable function detectClaudeCli() { - // Priority 1: CCS_CLAUDE_PATH environment variable + // Priority 1: CCS_CLAUDE_PATH environment variable (if user wants custom path) if (process.env.CCS_CLAUDE_PATH) { const ccsPath = expandPath(process.env.CCS_CLAUDE_PATH); - if (fs.existsSync(ccsPath) && isExecutable(ccsPath)) { + // Basic validation: file exists + if (fs.existsSync(ccsPath)) { return ccsPath; } - // Invalid CCS_CLAUDE_PATH - continue to fallbacks + // Invalid CCS_CLAUDE_PATH - show warning and fall back to PATH + console.warn('[!] Warning: CCS_CLAUDE_PATH is set but file not found:', ccsPath); + console.warn(' Falling back to system PATH lookup...'); } - // Priority 2: Check if claude in PATH - try { - const claudePath = execSync( - process.platform === 'win32' ? 'where claude' : 'which claude', - { encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] } - ).trim().split('\n')[0]; - - if (claudePath && fs.existsSync(claudePath)) { - return claudePath; - } - } catch (e) { - // Not in PATH, continue to common locations - } - - // Priority 3: Check common installation locations - const commonLocations = getCommonLocations(); - - for (const location of commonLocations) { - const expandedPath = expandPath(location); - if (fs.existsSync(expandedPath) && isExecutable(expandedPath)) { - return expandedPath; - } - } - - // Not found - return null; -} - -// Get platform-specific common locations -function getCommonLocations() { - const home = require('os').homedir(); - - if (process.platform === 'win32') { - return [ - path.join(process.env.LOCALAPPDATA || '', 'Claude', 'claude.exe'), - path.join(process.env.PROGRAMFILES || '', 'Claude', 'claude.exe'), - 'C:\\Program Files\\Claude\\claude.exe', - 'D:\\Program Files\\Claude\\claude.exe', - path.join(home, '.local', 'bin', 'claude.exe') - ]; - } else if (process.platform === 'darwin') { - return [ - '/usr/local/bin/claude', - path.join(home, '.local/bin/claude'), - '/opt/homebrew/bin/claude' - ]; - } else { - return [ - '/usr/local/bin/claude', - path.join(home, '.local/bin/claude'), - '/usr/bin/claude' - ]; - } -} - -// Check if file is executable -function isExecutable(filePath) { - try { - fs.accessSync(filePath, fs.constants.X_OK); - return true; - } catch (e) { - return false; - } -} - -// Validate Claude CLI path -function validateClaudeCli(claudePath) { - // Check 1: Empty path - if (!claudePath) { - throw new Error('No path provided'); - } - - // Check 2: File exists - if (!fs.existsSync(claudePath)) { - throw new Error(`File not found: ${claudePath}`); - } - - // Check 3: Is regular file (not directory) - const stats = fs.statSync(claudePath); - if (!stats.isFile()) { - throw new Error(`Path is a directory: ${claudePath}`); - } - - // Check 4: Is executable - if (!isExecutable(claudePath)) { - throw new Error(`File is not executable: ${claudePath}\n\nTry: chmod +x ${claudePath}`); - } - - // Check 5: Path safety (prevent injection) - if (!isPathSafe(claudePath)) { - throw new Error(`Path contains unsafe characters: ${claudePath}\n\nAllowed: alphanumeric, path separators, spaces, hyphens, underscores, dots`); - } - - return true; + // Priority 2: Use 'claude' from PATH (trust the system) + // This is the standard case - if user installed Claude CLI, it's in their PATH + return 'claude'; } // Show Claude not found error function showClaudeNotFoundError() { - const envVarStatus = process.env.CCS_CLAUDE_PATH || '(not set)'; const isWindows = process.platform === 'win32'; - const errorMsg = `Claude CLI not found + const errorMsg = `Claude CLI not found in PATH -Searched: - - CCS_CLAUDE_PATH: ${envVarStatus} - - System PATH: not found - - Common locations: not found +CCS requires Claude CLI to be installed and available in your PATH. Solutions: - 1. Add Claude CLI to PATH: - - ${isWindows - ? '# Find where Claude is installed\n Get-ChildItem -Path C:\\,D:\\ -Filter claude.exe -Recurse\n\n # Add to PATH\n $env:Path += \';D:\\path\\to\\claude\\directory\'\n [Environment]::SetEnvironmentVariable(\'Path\', $env:Path, \'User\')' - : '# Find where Claude is installed\n sudo find / -name claude 2>/dev/null\n\n # Add to PATH\n export PATH="/path/to/claude/bin:$PATH"\n echo \'export PATH="/path/to/claude/bin:$PATH"\' >> ~/.bashrc\n source ~/.bashrc' - } - - 2. Or set custom path: - - ${isWindows - ? '$env:CCS_CLAUDE_PATH = \'D:\\full\\path\\to\\claude.exe\'\n [Environment]::SetEnvironmentVariable(\'CCS_CLAUDE_PATH\', \'D:\\full\\path\\to\\claude.exe\', \'User\')' - : 'export CCS_CLAUDE_PATH="/full/path/to/claude"\n echo \'export CCS_CLAUDE_PATH="/full/path/to/claude"\' >> ~/.bashrc\n source ~/.bashrc' - } - - 3. Or install Claude CLI: - + 1. Install Claude CLI: https://docs.claude.com/en/docs/claude-code/installation -Verify installation: - ccs --version`; + 2. Verify installation: + ${isWindows ? 'Get-Command claude' : 'command -v claude'} + + 3. If installed but not in PATH, add it: + # Find Claude installation + ${isWindows ? 'where.exe claude' : 'which claude'} + + # Or set custom path + ${isWindows + ? '$env:CCS_CLAUDE_PATH = \'C:\\path\\to\\claude.exe\'' + : 'export CCS_CLAUDE_PATH=\'/path/to/claude\'' + } + +Restart your terminal after installation.`; showError(errorMsg); } module.exports = { detectClaudeCli, - validateClaudeCli, showClaudeNotFoundError }; \ No newline at end of file diff --git a/installers/install.ps1 b/installers/install.ps1 index 1bc3d626..f049c98a 100644 --- a/installers/install.ps1 +++ b/installers/install.ps1 @@ -30,7 +30,7 @@ $InstallMethod = if ($ScriptDir -and ((Test-Path "$ScriptDir\lib\ccs.ps1") -or ( # IMPORTANT: Update this version when releasing new versions! # This hardcoded version is used for standalone installations (irm | iex) # For git installations, VERSION file is read if available -$CcsVersion = "2.4.0" +$CcsVersion = "2.4.1" # Try to read VERSION file for git installations if ($ScriptDir) { diff --git a/installers/install.sh b/installers/install.sh index c7faf889..e57d0a93 100755 --- a/installers/install.sh +++ b/installers/install.sh @@ -31,7 +31,7 @@ fi # IMPORTANT: Update this version when releasing new versions! # This hardcoded version is used for standalone installations (curl | bash) # For git installations, VERSION file is read if available -CCS_VERSION="2.4.0" +CCS_VERSION="2.4.1" # Try to read VERSION file for git installations if [[ -f "$SCRIPT_DIR/VERSION" ]]; then diff --git a/lib/ccs b/lib/ccs index fd9c0b62..3f918d4c 100755 --- a/lib/ccs +++ b/lib/ccs @@ -2,7 +2,7 @@ set -euo pipefail # Version (updated by scripts/bump-version.sh) -CCS_VERSION="2.4.0" +CCS_VERSION="2.4.1" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # --- Color/Format Functions --- @@ -32,147 +32,44 @@ setup_colors # --- Claude CLI Detection Logic --- detect_claude_cli() { - local claude_path="" - - # Priority 1: CCS_CLAUDE_PATH environment variable + # Priority 1: CCS_CLAUDE_PATH environment variable (if user wants custom path) if [[ -n "${CCS_CLAUDE_PATH:-}" ]]; then - if [[ -f "$CCS_CLAUDE_PATH" ]] && [[ -x "$CCS_CLAUDE_PATH" ]]; then + # Basic validation: file exists + if [[ -f "$CCS_CLAUDE_PATH" ]]; then echo "$CCS_CLAUDE_PATH" return 0 fi - # Invalid CCS_CLAUDE_PATH - continue to fallbacks - # Warning will be shown later in validation phase + # Invalid CCS_CLAUDE_PATH - show warning and fall back to PATH + echo "[!] Warning: CCS_CLAUDE_PATH is set but file not found: $CCS_CLAUDE_PATH" >&2 + echo " Falling back to system PATH lookup..." >&2 fi - # Priority 2: Check if claude in PATH - claude_path=$(command -v claude 2>/dev/null || true) - if [[ -n "$claude_path" ]]; then - echo "$claude_path" - return 0 - fi - - # Priority 3: Check common installation locations - local common_locations=() - - # Platform-specific common locations - if [[ "$OSTYPE" == darwin* ]]; then - # macOS - common_locations=( - "/usr/local/bin/claude" - "$HOME/.local/bin/claude" - "/opt/homebrew/bin/claude" - ) - else - # Linux - common_locations=( - "/usr/local/bin/claude" - "$HOME/.local/bin/claude" - "/usr/bin/claude" - ) - fi - - # Check each common location - for location in "${common_locations[@]}"; do - if [[ -f "$location" ]] && [[ -x "$location" ]]; then - echo "$location" - return 0 - fi - done - - # Not found - echo "" - return 1 -} - -# Global variable for validation error message -VALIDATION_ERROR="" - -validate_claude_cli() { - local path="$1" - VALIDATION_ERROR="" - - # Check 1: Empty path - if [[ -z "$path" ]]; then - VALIDATION_ERROR="No path provided" - return 1 - fi - - # Check 2: File exists - if [[ ! -e "$path" ]]; then - VALIDATION_ERROR="File not found: $path" - return 1 - fi - - # Check 3: Is regular file (not directory) - if [[ -d "$path" ]]; then - VALIDATION_ERROR="Path is a directory: $path" - return 1 - fi - - # Check 4: Is executable - if [[ ! -x "$path" ]]; then - VALIDATION_ERROR="File is not executable: $path - -Try: chmod +x $path" - return 1 - fi - - # Check 5: Path safety (prevent injection) - # Allow: alphanumeric, /, \, :, space, -, _, ., ~ - if [[ "$path" =~ [^\;a-zA-Z0-9/\\:\ \._~-] ]]; then - VALIDATION_ERROR="Path contains unsafe characters: $path - -Allowed: alphanumeric, path separators, spaces, hyphens, underscores, dots" - return 1 - fi - - # All checks passed + # Priority 2: Use 'claude' from PATH (trust the system) + # This is the standard case - if user installed Claude CLI, it's in their PATH + echo "claude" return 0 } show_claude_not_found_error() { - local env_var_status="${CCS_CLAUDE_PATH:-(not set)}" + msg_error "Claude CLI not found in PATH - msg_error "Claude CLI not found - -Searched: - - CCS_CLAUDE_PATH: $env_var_status - - System PATH: not found - - Common locations: not found +CCS requires Claude CLI to be installed and available in your PATH. Solutions: - 1. Add Claude CLI to PATH: - - # Find where Claude is installed - sudo find / -name claude 2>/dev/null - - # Then add to PATH (replace /path/to with actual path) - export PATH=\"/path/to/claude/bin:\$PATH\" - echo 'export PATH=\"/path/to/claude/bin:\$PATH\"' >> ~/.bashrc - source ~/.bashrc - - 2. Or set custom path: - - export CCS_CLAUDE_PATH=\"/full/path/to/claude\" - echo 'export CCS_CLAUDE_PATH=\"/full/path/to/claude\"' >> ~/.bashrc - source ~/.bashrc - - Example (D drive on Windows/WSL): - export CCS_CLAUDE_PATH=\"/mnt/d/Tools/Claude/claude.exe\" - - 3. Or install Claude CLI: - + 1. Install Claude CLI: https://docs.claude.com/en/docs/claude-code/installation -Verify installation: - ccs --version + 2. Verify installation: + command -v claude -Debugging: - # Check if claude command exists - command -v claude + 3. If installed but not in PATH, add it: + # Find Claude installation + which claude - # Check CCS_CLAUDE_PATH - echo \$CCS_CLAUDE_PATH" + # Or set custom path + export CCS_CLAUDE_PATH='/path/to/claude' + +Restart your terminal after installation." } CONFIG_FILE="${CCS_CONFIG:-$HOME/.ccs/config.json}" @@ -387,20 +284,12 @@ fi # Special case: help command (check BEFORE profile detection) if [[ $# -gt 0 ]] && [[ "${1}" == "--help" || "${1}" == "-h" || "${1}" == "help" ]]; then shift # Remove the help argument - - # Detect and validate Claude CLI for help command CLAUDE_CLI=$(detect_claude_cli) - if [[ -z "$CLAUDE_CLI" ]]; then + + if ! exec "$CLAUDE_CLI" --help "$@"; then show_claude_not_found_error exit 1 fi - - if ! validate_claude_cli "$CLAUDE_CLI"; then - msg_error "$VALIDATION_ERROR" - exit 1 - fi - - exec "$CLAUDE_CLI" --help "$@" fi # Special case: install command (check BEFORE profile detection) @@ -517,16 +406,9 @@ fi # Detect Claude CLI executable CLAUDE_CLI=$(detect_claude_cli) -if [[ -z "$CLAUDE_CLI" ]]; then +# Execute Claude with the profile settings +# If claude is not found, exec will fail and show an error +if ! exec "$CLAUDE_CLI" --settings "$SETTINGS_PATH" "$@"; then show_claude_not_found_error exit 1 fi - -# Validate detected path -if ! validate_claude_cli "$CLAUDE_CLI"; then - msg_error "$VALIDATION_ERROR" - exit 1 -fi - -# Execute with validated path -exec "$CLAUDE_CLI" --settings "$SETTINGS_PATH" "$@" diff --git a/lib/ccs.ps1 b/lib/ccs.ps1 index a1e97cc6..ebebca74 100644 --- a/lib/ccs.ps1 +++ b/lib/ccs.ps1 @@ -30,136 +30,49 @@ function Find-ClaudeCli { [OutputType([string])] param() - # Priority 1: CCS_CLAUDE_PATH environment variable + # Priority 1: CCS_CLAUDE_PATH environment variable (if user wants custom path) $CcsClaudePath = $env:CCS_CLAUDE_PATH if ($CcsClaudePath) { - if ((Test-Path $CcsClaudePath -PathType Leaf) -and - (Get-Command $CcsClaudePath -ErrorAction SilentlyContinue)) { + # Basic validation: file exists + if (Test-Path $CcsClaudePath -PathType Leaf) { return $CcsClaudePath } - # Invalid CCS_CLAUDE_PATH - continue to fallbacks - # Warning will be shown later in validation phase + # Invalid CCS_CLAUDE_PATH - show warning and fall back to PATH + Write-Host "[!] Warning: CCS_CLAUDE_PATH is set but file not found: $CcsClaudePath" -ForegroundColor Yellow + Write-Host " Falling back to system PATH lookup..." -ForegroundColor Yellow } - # Priority 2: Check if claude in PATH - $ClaudeInPath = Get-Command claude -ErrorAction SilentlyContinue - if ($ClaudeInPath) { - return $ClaudeInPath.Source - } - - # Priority 3: Check common installation locations - $CommonLocations = @( - "$env:LOCALAPPDATA\Claude\claude.exe", - "$env:PROGRAMFILES\Claude\claude.exe", - "C:\Program Files\Claude\claude.exe", - "D:\Program Files\Claude\claude.exe", - "$env:USERPROFILE\.local\bin\claude.exe" - ) - - foreach ($Location in $CommonLocations) { - $ExpandedPath = [System.Environment]::ExpandEnvironmentVariables($Location) - if ((Test-Path $ExpandedPath -PathType Leaf) -and - (Get-Command $ExpandedPath -ErrorAction SilentlyContinue)) { - return $ExpandedPath - } - } - - # Not found - return "" -} - -function Test-ClaudeCli { - [OutputType([bool])] - param( - [Parameter(Mandatory=$true)] - [AllowEmptyString()] - [string]$Path - ) - - # Check 1: Empty path - if ([string]::IsNullOrWhiteSpace($Path)) { - throw "No path provided" - } - - # Check 2: File exists - if (-not (Test-Path $Path)) { - throw "File not found: $Path" - } - - # Check 3: Is regular file (not directory) - if (Test-Path $Path -PathType Container) { - throw "Path is a directory: $Path" - } - - # Check 4: Is executable (Get-Command can load it) - try { - $null = Get-Command $Path -ErrorAction Stop - } catch { - throw "File is not executable: $Path`n`nCheck file permissions and file type" - } - - # Check 5: Path safety (prevent injection) - # Allow: alphanumeric, \, /, :, space, -, _, ., ~ - if ($Path -match '[;\|&<>``\$\*\?\[\]''\"()]') { - throw "Path contains unsafe characters: $Path`n`nAllowed: alphanumeric, path separators, spaces, hyphens, underscores, dots" - } - - # All checks passed - return $true + # Priority 2: Use 'claude' from PATH (trust the system) + # This is the standard case - if user installed Claude CLI, it's in their PATH + return "claude" } function Show-ClaudeNotFoundError { - $EnvVarStatus = if ($env:CCS_CLAUDE_PATH) { $env:CCS_CLAUDE_PATH } else { "(not set)" } - Write-ErrorMsg @" -Claude CLI not found +Claude CLI not found in PATH -Searched: - - CCS_CLAUDE_PATH: $EnvVarStatus - - System PATH: not found - - Common locations: not found +CCS requires Claude CLI to be installed and available in your PATH. Solutions: - 1. Add Claude CLI to PATH: - - # Find where Claude is installed - Get-ChildItem -Path C:\,D:\ -Filter claude.exe -Recurse -ErrorAction SilentlyContinue | Select-Object FullName - - # Then add to PATH (replace with actual path) - `$env:Path += ';D:\path\to\claude\directory' - [Environment]::SetEnvironmentVariable('Path', `$env:Path, 'User') - - # Restart terminal for changes to take effect - - 2. Or set custom path: - - `$env:CCS_CLAUDE_PATH = 'D:\full\path\to\claude.exe' - [Environment]::SetEnvironmentVariable('CCS_CLAUDE_PATH', 'D:\full\path\to\claude.exe', 'User') - - Example (D drive installation): - `$env:CCS_CLAUDE_PATH = 'D:\Tools\Claude\claude.exe' - [Environment]::SetEnvironmentVariable('CCS_CLAUDE_PATH', 'D:\Tools\Claude\claude.exe', 'User') - - # Restart terminal for changes to take effect - - 3. Or install Claude CLI: - + 1. Install Claude CLI: https://docs.claude.com/en/docs/claude-code/installation -Verify installation: - ccs --version + 2. Verify installation: + Get-Command claude -Debugging: - # Check if claude command exists - Get-Command claude -ErrorAction SilentlyContinue + 3. If installed but not in PATH, add it: + # Find Claude installation + where.exe claude - # Check CCS_CLAUDE_PATH - `$env:CCS_CLAUDE_PATH + # Or set custom path + `$env:CCS_CLAUDE_PATH = 'C:\path\to\claude.exe' + +Restart your terminal after installation. "@ } # Version (updated by scripts/bump-version.sh) -$CcsVersion = "2.4.0" +$CcsVersion = "2.4.1" $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path # Installation function for commands and skills @@ -382,21 +295,8 @@ if ($FirstArg -eq "version" -or $FirstArg -eq "--version" -or $FirstArg -eq "-v" # Special case: help command (check BEFORE profile detection) if ($FirstArg -eq "--help" -or $FirstArg -eq "-h" -or $FirstArg -eq "help") { - # Detect and validate Claude CLI for help command $ClaudeCli = Find-ClaudeCli - if ([string]::IsNullOrEmpty($ClaudeCli)) { - Show-ClaudeNotFoundError - exit 1 - } - - try { - $null = Test-ClaudeCli -Path $ClaudeCli - } catch { - Write-ErrorMsg $_.Exception.Message - exit 1 - } - try { if ($RemainingArgs) { & $ClaudeCli --help @RemainingArgs @@ -405,8 +305,7 @@ if ($FirstArg -eq "--help" -or $FirstArg -eq "-h" -or $FirstArg -eq "help") { } exit $LASTEXITCODE } catch { - Write-Host "Error: Failed to execute claude --help" -ForegroundColor Red - Write-Host $_.Exception.Message + Show-ClaudeNotFoundError exit 1 } } @@ -580,20 +479,7 @@ Solutions: # Detect Claude CLI executable $ClaudeCli = Find-ClaudeCli -if ([string]::IsNullOrEmpty($ClaudeCli)) { - Show-ClaudeNotFoundError - exit 1 -} - -# Validate detected path -try { - $null = Test-ClaudeCli -Path $ClaudeCli -} catch { - Write-ErrorMsg $_.Exception.Message - exit 1 -} - -# Execute with validated path +# Execute Claude with the profile settings try { if ($RemainingArgs) { & $ClaudeCli --settings $SettingsPath @RemainingArgs @@ -602,7 +488,6 @@ try { } exit $LASTEXITCODE } catch { - Write-Host "Error: Failed to execute claude" -ForegroundColor Red - Write-Host $_.Exception.Message + Show-ClaudeNotFoundError exit 1 } diff --git a/package.json b/package.json index 1241a002..abfe2d5e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "@kai/ccs", - "version": "2.4.0", + "name": "@kaitranntt/ccs", + "version": "2.4.1", "description": "Claude Code Switch - Instant profile switching between Claude Sonnet 4.5 and GLM 4.6", "keywords": [ "cli",