Last active
July 18, 2025 17:05
-
-
Save paulbrodner/3b055641086b4e10f718c36853d2702f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| #============================================================================== | |
| # Claude Code Quick Start Setup Script | |
| #============================================================================== | |
| # | |
| # DESCRIPTION: | |
| # This script automates the setup of Claude Code on macOS, including all | |
| # required dependencies and configuration. It installs Homebrew, Node.js, | |
| # AWS CLI, Claude Code, and configures AWS SSO integration. | |
| # | |
| # AUTHOR: | |
| # Paul Brodner | |
| # | |
| # VERSION: | |
| # 1.0.0 | |
| # | |
| # REQUIREMENTS: | |
| # - macOS (Darwin) | |
| # - Internet connection | |
| # - Administrative privileges for some installations | |
| # | |
| # USAGE: | |
| # # Direct execution: | |
| # bash setup-claude-code.sh | |
| # | |
| # # Install via curl: | |
| # bash <(curl -sSL https://gist.githubusercontent.com/paulbrodner/3b055641086b4e10f718c36853d2702f/raw) | |
| # | |
| # WHAT THIS SCRIPT DOES: | |
| # 1. Checks for and installs Homebrew (if needed) | |
| # 2. Installs Node.js and npm (if needed) | |
| # 3. Installs AWS CLI (if needed) | |
| # 4. Installs/updates Claude Code | |
| # 5. Configures AWS SSO integration | |
| # 6. Sets up Claude Code configuration with Bedrock | |
| # 7. Creates AWS credentials helper alias | |
| # | |
| # NOTES: | |
| # - The script will prompt before installing each component | |
| # - AWS SSO configuration requires manual input | |
| # - Creates ~/.claude/settings.json with optimized settings | |
| # - Adds 'claude-aws' alias to your shell configuration | |
| # USAGE: | |
| # in a terminal call: claude-aws (and you are set) | |
| #============================================================================== | |
| # Colors for output | |
| GREEN='\033[0;32m' | |
| YELLOW='\033[1;33m' | |
| RED='\033[0;31m' | |
| NC='\033[0m' # No Color | |
| BLUE='\033[0;34m' | |
| # Print banner | |
| echo -e "${BLUE}" | |
| echo " ▗▄▄▖▗▖ ▗▄▖ ▗▖ ▗▖▗▄▄▄ ▗▄▄▄▖ ▗▄▄▖ ▗▄▖ ▗▄▄▄ ▗▄▄▄▖ ▗▄▄▄▖ ▗▖ ▗▖▗▄▄▄▖ ▗▄▄▖▗▖ ▗▖ ▗▄▄▖▗▄▄▄▖▗▄▖ ▗▄▄▖▗▄▄▄▖" | |
| echo "▐▌ ▐▌ ▐▌ ▐▌▐▌ ▐▌▐▌ █▐▌ ▐▌ ▐▌ ▐▌▐▌ █▐▌ ▐▌ ▐▌ ▐▌ ▐▌ █ ▐▌ ▐▌▗▞▘ ▐▌ █ ▐▌ ▐▌▐▌ ▐▌ █ " | |
| echo "▐▌ ▐▌ ▐▛▀▜▌▐▌ ▐▌▐▌ █▐▛▀▀▘ ▐▌ ▐▌ ▐▌▐▌ █▐▛▀▀▘ ▐▌ ▐▌ ▐▌ ▐▌ █ ▐▌ ▐▛▚▖ ▝▀▚▖ █ ▐▛▀▜▌▐▛▀▚▖ █ " | |
| echo "▝▚▄▄▖▐▙▄▄▖▐▌ ▐▌▝▚▄▞▘▐▙▄▄▀▐▙▄▄▖ ▝▚▄▄▖▝▚▄▞▘▐▙▄▄▀▐▙▄▄▖ ▐▙▄▟▙▖▝▚▄▞▘▗▄█▄▖▝▚▄▄▖▐▌ ▐▌ ▗▄▄▞▘ █ ▐▌ ▐▌▐▌ ▐▌ █ " | |
| echo -e "${NC}" | |
| echo -e "${GREEN}Welcome to the Claude Code Quick Start Setup!${NC}" | |
| echo "This wizard will help you set up Claude Code on your Mac." | |
| echo " (by Paul Brodner)" | |
| echo | |
| # Check if running on macOS | |
| if [[ $(uname) != "Darwin" ]]; then | |
| echo -e "${RED}This script is designed for macOS only.${NC}" | |
| exit 1 | |
| fi | |
| # Function to check if a command exists | |
| command_exists() { | |
| command -v "$1" >/dev/null 2>&1 | |
| } | |
| # Function to prompt user for yes/no | |
| prompt_yes_no() { | |
| while true; do | |
| read -p "$1 (y/n): " yn | |
| case $yn in | |
| [Yy]* ) return 0;; | |
| [Nn]* ) return 1;; | |
| * ) echo "Please answer yes (y) or no (n).";; | |
| esac | |
| done | |
| } | |
| # Check if Homebrew is installed | |
| echo -e "${BLUE}Checking for Homebrew...${NC}" | |
| if command_exists brew; then | |
| echo -e "${GREEN}✓ Homebrew is installed.${NC}" | |
| else | |
| echo -e "${YELLOW}Homebrew is not installed.${NC}" | |
| if prompt_yes_no "Would you like to install Homebrew?"; then | |
| echo "Installing Homebrew..." | |
| /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" | |
| if [ $? -ne 0 ]; then | |
| echo -e "${RED}Failed to install Homebrew. Please install it manually.${NC}" | |
| exit 1 | |
| fi | |
| echo -e "${GREEN}✓ Homebrew installed successfully.${NC}" | |
| else | |
| echo -e "${RED}Homebrew is required for this setup. Exiting.${NC}" | |
| exit 1 | |
| fi | |
| fi | |
| # Check for Node.js and npm | |
| echo -e "${BLUE}Checking for Node.js and npm...${NC}" | |
| if command_exists node && command_exists npm; then | |
| NODE_VERSION=$(node -v) | |
| NPM_VERSION=$(npm -v) | |
| echo -e "${GREEN}✓ Node.js ${NODE_VERSION} and npm ${NPM_VERSION} are installed.${NC}" | |
| else | |
| echo -e "${YELLOW}Node.js and/or npm are not installed.${NC}" | |
| if prompt_yes_no "Would you like to install Node.js?"; then | |
| echo "Installing Node.js using Homebrew..." | |
| brew install node | |
| if [ $? -ne 0 ]; then | |
| echo -e "${RED}Failed to install Node.js. Please install it manually.${NC}" | |
| exit 1 | |
| fi | |
| NODE_VERSION=$(node -v) | |
| NPM_VERSION=$(npm -v) | |
| echo -e "${GREEN}✓ Node.js ${NODE_VERSION} and npm ${NPM_VERSION} installed successfully.${NC}" | |
| else | |
| echo -e "${RED}Node.js is required for Claude Code. Exiting.${NC}" | |
| exit 1 | |
| fi | |
| fi | |
| # Check for AWS CLI | |
| echo -e "${BLUE}Checking for AWS CLI...${NC}" | |
| if command_exists aws; then | |
| AWS_VERSION=$(aws --version) | |
| echo -e "${GREEN}✓ AWS CLI is installed: ${AWS_VERSION}${NC}" | |
| else | |
| echo -e "${YELLOW}AWS CLI is not installed.${NC}" | |
| if prompt_yes_no "Would you like to install AWS CLI?"; then | |
| echo "Installing AWS CLI using Homebrew..." | |
| brew install awscli | |
| if [ $? -ne 0 ]; then | |
| echo -e "${RED}Failed to install AWS CLI. Please install it manually.${NC}" | |
| exit 1 | |
| fi | |
| AWS_VERSION=$(aws --version) | |
| echo -e "${GREEN}✓ AWS CLI installed successfully: ${AWS_VERSION}${NC}" | |
| else | |
| echo -e "${RED}AWS CLI is required for Claude Code. Exiting.${NC}" | |
| exit 1 | |
| fi | |
| fi | |
| # Install Claude Code | |
| echo -e "${BLUE}Checking for Claude Code...${NC}" | |
| if command_exists claude; then | |
| CLAUDE_CODE_VERSION=$(claude --version || echo "Version unknown") | |
| echo -e "${GREEN}✓ Claude Code is installed: ${CLAUDE_CODE_VERSION}${NC}" | |
| if prompt_yes_no "Would you like to update Claude Code to the latest version?"; then | |
| echo "Updating Claude Code..." | |
| npm install -g @anthropic-ai/claude-code | |
| if [ $? -ne 0 ]; then | |
| echo -e "${RED}Failed to update Claude Code.${NC}" | |
| else | |
| echo -e "${GREEN}✓ Claude Code updated successfully.${NC}" | |
| fi | |
| fi | |
| else | |
| echo -e "${YELLOW}Claude Code is not installed.${NC}" | |
| if prompt_yes_no "Would you like to install Claude Code?"; then | |
| echo "Installing Claude Code..." | |
| npm install -g @anthropic-ai/claude-code | |
| if [ $? -ne 0 ]; then | |
| echo -e "${RED}Failed to install Claude Code. Please try again or install it manually.${NC}" | |
| exit 1 | |
| fi | |
| echo -e "${GREEN}✓ Claude Code installed successfully.${NC}" | |
| else | |
| echo -e "${RED}Claude Code installation is required. Exiting.${NC}" | |
| exit 1 | |
| fi | |
| fi | |
| # AWS SSO Configuration | |
| echo -e "${BLUE}Setting up AWS credentials...${NC}" | |
| echo "You'll need to configure AWS SSO for Claude Code to work properly." | |
| if prompt_yes_no "Would you like to configure AWS SSO now?"; then | |
| echo "Running 'aws configure sso'..." | |
| echo "(At the end you can set the Profile Name 'default' to simplify things!)" | |
| aws configure sso | |
| if [ $? -ne 0 ]; then | |
| echo -e "${RED}AWS SSO configuration failed.${NC}" | |
| echo "You can configure it manually later using 'aws configure sso'" | |
| else | |
| echo -e "${GREEN}✓ AWS SSO configured successfully.${NC}" | |
| fi | |
| else | |
| echo "You'll need to configure AWS SSO manually using 'aws configure sso'" | |
| fi | |
| # Claude Settings Configuration | |
| echo -e "${BLUE}Setting up Claude Code configuration...${NC}" | |
| # Create ~/.claude directory if it doesn't exist | |
| CLAUDE_CONFIG_DIR="$HOME/.claude" | |
| CLAUDE_SETTINGS_FILE="$CLAUDE_CONFIG_DIR/settings.json" | |
| if [ ! -d "$CLAUDE_CONFIG_DIR" ]; then | |
| echo "Creating Claude configuration directory..." | |
| mkdir -p "$CLAUDE_CONFIG_DIR" | |
| fi | |
| # Create or update settings.json | |
| echo "Creating/updating Claude settings file: $CLAUDE_SETTINGS_FILE" | |
| cat > "$CLAUDE_SETTINGS_FILE" << 'EOL' | |
| { | |
| "env": { | |
| "CLAUDE_CODE_ENABLE_TELEMETRY": "0", | |
| "DISABLE_TELEMETRY": 1, | |
| "CLAUDE_CODE_USE_BEDROCK": "1", | |
| "AWS_REGION": "us-east-1", | |
| "AWS_BEDROCK_CROSS_REGION": "true", | |
| "ANTHROPIC_MODEL": "us.anthropic.claude-3-7-sonnet-20250219-v1:0", | |
| "ANTHROPIC_SMALL_FAST_MODEL": "us.anthropic.claude-3-haiku-20240307-v1:0" | |
| } | |
| } | |
| EOL | |
| echo -e "${GREEN}✓ Claude settings file created: $CLAUDE_SETTINGS_FILE${NC}" | |
| # AWS Credentials Helper Function | |
| echo -e "${BLUE}Setting up AWS credentials helper...${NC}" | |
| # Detect user's shell | |
| USER_SHELL="$(basename "$SHELL")" | |
| echo -e "Detected shell: ${YELLOW}$USER_SHELL${NC}" | |
| # Determine the appropriate config file based on shell | |
| SHELL_CONFIG="" | |
| SHELL_SOURCE_CMD="" | |
| case "$USER_SHELL" in | |
| "zsh") | |
| SHELL_CONFIG="$HOME/.zshrc" | |
| SHELL_SOURCE_CMD="source $SHELL_CONFIG" | |
| ;; | |
| "bash") | |
| # Check if .bash_profile exists, otherwise use .bashrc | |
| if [ -f "$HOME/.bash_profile" ]; then | |
| SHELL_CONFIG="$HOME/.bash_profile" | |
| else | |
| SHELL_CONFIG="$HOME/.bashrc" | |
| fi | |
| SHELL_SOURCE_CMD="source $SHELL_CONFIG" | |
| ;; | |
| *) | |
| # Fall back to .profile for other shells | |
| SHELL_CONFIG="$HOME/.profile" | |
| SHELL_SOURCE_CMD="source $SHELL_CONFIG" | |
| ;; | |
| esac | |
| echo "Adding Claude alias to $SHELL_CONFIG" | |
| # Check if the alias already exists | |
| if grep -q "alias claude-aws=" "$SHELL_CONFIG" 2>/dev/null; then | |
| echo -e "${YELLOW}Claude AWS alias already exists in $SHELL_CONFIG. Skipping.${NC}" | |
| else | |
| cat >> "$SHELL_CONFIG" << 'EOL' | |
| # Claude Code with AWS credentials helper | |
| claudeaws() { | |
| local profile="default" | |
| local args=() | |
| local token_duration=12600 # 3 hours and 30 minutes | |
| local status_file="$HOME/.claude-aws" | |
| # Check if first argument looks like a profile name (not starting with -) | |
| if [ $# -gt 0 ] && [[ ! "$1" = -* ]]; then | |
| profile="$1" | |
| shift | |
| fi | |
| # Collect remaining arguments | |
| while [ $# -gt 0 ]; do | |
| args+=("$1") | |
| shift | |
| done | |
| echo "Using AWS profile: $profile" | |
| echo "Stand by..." | |
| # Try to export credentials, handle SSO token expiration | |
| aws_output=$(aws configure export-credentials --profile "$profile" --format env 2>&1) | |
| aws_exit_code=$? | |
| # Check if command failed or contains SSO token error | |
| if [ $aws_exit_code -ne 0 ] || echo "$aws_output" | grep -q "Token has expired\|refresh failed\|SSO session"; then | |
| echo "AWS credentials expired, attempting to refresh SSO token..." | |
| if aws sso login --profile "$profile"; then | |
| echo "SSO login successful, retrying credential export..." | |
| eval "$(aws configure export-credentials --profile "$profile" --format env)" | |
| else | |
| echo "Failed to refresh SSO token. Please check your AWS configuration." | |
| return 1 | |
| fi | |
| else | |
| # Credentials were valid, use them | |
| eval "$aws_output" | |
| fi | |
| # Start background timer if not already running | |
| if [ ! -f "$status_file" ]; then | |
| echo "Starting AWS token expiration timer..." | |
| # Create status file with timestamp and profile | |
| echo "$(date '+%s'):$$:$profile" > "$status_file" | |
| # Start background process | |
| ( | |
| sleep "$token_duration" | |
| # Check if status file still exists (in case user manually cleaned up) | |
| if [ -f "$status_file" ]; then | |
| osascript -e 'display dialog "AWS tokens are about to expire!" with title "Claude AWS Alert" buttons {"OK"} default button "OK" with icon caution' | |
| rm -f "$status_file" | |
| fi | |
| ) & | |
| local bg_pid=$! | |
| echo "Timer started (PID: $bg_pid) - will notify in $token_duration seconds" | |
| else | |
| echo "AWS token timer already running (status file exists at $status_file)" | |
| # Show current timer info | |
| if [ -r "$status_file" ]; then | |
| local status_content=$(cat "$status_file") | |
| local start_time=$(echo "$status_content" | cut -d: -f1) | |
| local current_time=$(date '+%s') | |
| local elapsed=$((current_time - start_time)) | |
| local remaining=$((token_duration - elapsed)) | |
| if [ $remaining -gt 0 ]; then | |
| echo "Timer has $remaining seconds remaining" | |
| else | |
| echo "Timer should have already expired - cleaning up stale status file" | |
| rm -f "$status_file" | |
| fi | |
| fi | |
| fi | |
| claude "${args[@]}" | |
| } | |
| # Helper function to check timer status | |
| claude-aws-status() { | |
| local status_file="$HOME/.claude-aws" | |
| if [ -f "$status_file" ]; then | |
| local status_content=$(cat "$status_file") | |
| local start_time=$(echo "$status_content" | cut -d: -f1) | |
| local pid=$(echo "$status_content" | cut -d: -f2) | |
| local profile=$(echo "$status_content" | cut -d: -f3) | |
| local current_time=$(date '+%s') | |
| local elapsed=$((current_time - start_time)) | |
| echo "AWS token timer is running:" | |
| echo " Profile: $profile" | |
| echo " Started: $(date -r $start_time)" | |
| echo " Elapsed: ${elapsed}s" | |
| echo " Background PID: $pid" | |
| # Check if process is still running | |
| if ! kill -0 "$pid" 2>/dev/null; then | |
| echo " Warning: Background process is not running!" | |
| fi | |
| else | |
| echo "No AWS token timer is currently running" | |
| fi | |
| } | |
| # Helper function to stop/reset timer | |
| claude-aws-reset() { | |
| local status_file="$HOME/.claude-aws" | |
| if [ -f "$status_file" ]; then | |
| local status_content=$(cat "$status_file") | |
| local pid=$(echo "$status_content" | cut -d: -f2) | |
| # Try to kill the background process | |
| if kill -0 "$pid" 2>/dev/null; then | |
| kill "$pid" 2>/dev/null | |
| echo "Stopped background timer process (PID: $pid)" | |
| fi | |
| rm -f "$status_file" | |
| echo "Cleared timer status file" | |
| else | |
| echo "No timer to reset" | |
| fi | |
| } | |
| alias claude-aws=claudeaws | |
| EOL | |
| echo -e "${GREEN}✓ Claude AWS alias added to $SHELL_CONFIG${NC}" | |
| echo "Run '$SHELL_SOURCE_CMD' to activate the new alias" | |
| fi | |
| # Instructions for the user | |
| echo | |
| echo -e "${GREEN}================================${NC}" | |
| echo -e "${GREEN}Claude Code Setup Complete!${NC}" | |
| echo -e "${GREEN}================================${NC}" | |
| echo | |
| echo "To start using Claude Code:" | |
| echo | |
| echo -e "1. ${YELLOW}Run AWS SSO login:${NC}" | |
| echo " aws sso login" | |
| echo | |
| echo -e "2. ${YELLOW}Start Claude Code with AWS credentials:${NC}" | |
| echo " claude-aws # Uses default AWS profile" | |
| echo " claude-aws profile # Uses specified AWS profile" | |
| echo | |
| echo -e "3. ${YELLOW}Or start Claude Code without AWS credentials helper:${NC}" | |
| echo " claude" | |
| echo | |
| echo -e "${GREEN}Happy coding with Claude!${NC}" | |
| # Suggest sourcing shell config | |
| echo | |
| echo -e "${YELLOW}NOTE: To use the claude-aws command in this session, run:${NC}" | |
| echo " You can also start a new shell session to use the claude-aws command." | |
| echo "$SHELL_SOURCE_CMD" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment