diff --git a/lib/cli.sh b/lib/cli.sh index c70d574..8478b6c 100644 --- a/lib/cli.sh +++ b/lib/cli.sh @@ -31,7 +31,7 @@ parse_cli_args() { # Single parsing loop - each arg goes into exactly ONE bucket local found_script_command=false - for arg in "${all_args[@]}"; do + for arg in "${all_args[@]:-}"; do if [[ " ${HOST_ONLY_FLAGS[*]} " == *" $arg "* ]]; then # Bucket 1: Host-only flags host_flags+=("$arg") @@ -49,15 +49,15 @@ parse_cli_args() { done # Export results for use by main script - export CLI_HOST_FLAGS=("${host_flags[@]}") - export CLI_CONTROL_FLAGS=("${control_flags[@]}") + export CLI_HOST_FLAGS=("${host_flags[@]:-}") + export CLI_CONTROL_FLAGS=("${control_flags[@]:-}") export CLI_SCRIPT_COMMAND="$script_command" - export CLI_PASS_THROUGH=("${pass_through[@]}") + export CLI_PASS_THROUGH=("${pass_through[@]:-}") } # Process host-only flags and set environment variables process_host_flags() { - for flag in "${CLI_HOST_FLAGS[@]}"; do + for flag in "${CLI_HOST_FLAGS[@]:-}"; do case "$flag" in --verbose) export VERBOSE=true diff --git a/lib/commands.clean.sh b/lib/commands.clean.sh index 775c921..4735b03 100644 --- a/lib/commands.clean.sh +++ b/lib/commands.clean.sh @@ -89,7 +89,7 @@ _cmd_clean() { cecho "Multiple projects found matching '$search':" "$YELLOW" echo local i=1 - for match in "${matches[@]}"; do + for match in "${matches[@]:-}"; do local path="${match%|*}" local name="${match#*|}" printf " %d. %s (%s)\n" "$i" "$name" "$path" diff --git a/lib/commands.core.sh b/lib/commands.core.sh index 08d97bc..d3ba982 100644 --- a/lib/commands.core.sh +++ b/lib/commands.core.sh @@ -117,7 +117,7 @@ _cmd_shell() { echo "[DEBUG] Remaining args after processing: $*" >&2 fi # Don't pass any remaining arguments - only shell and the flags - run_claudebox_container "$temp_container" "interactive" shell "${shell_flags[@]}" + run_claudebox_container "$temp_container" "interactive" shell "${shell_flags[@]:-}" # Commit changes back to image fillbar @@ -127,7 +127,7 @@ _cmd_shell() { success "Changes saved to image!" else # Regular shell mode - just run without committing - run_claudebox_container "" "interactive" shell "${shell_flags[@]}" + run_claudebox_container "" "interactive" shell "${shell_flags[@]:-}" fi exit 0 diff --git a/lib/commands.profile.sh b/lib/commands.profile.sh index 462d5d4..0321575 100644 --- a/lib/commands.profile.sh +++ b/lib/commands.profile.sh @@ -127,7 +127,7 @@ _cmd_add() { [[ ${#selected[@]} -eq 0 ]] && error "No valid profiles specified\nRun 'claudebox profiles' to see available profiles" - update_profile_section "$profile_file" "profiles" "${selected[@]}" + update_profile_section "$profile_file" "profiles" "${selected[@]:-}" local all_profiles=() local all_profiles=() @@ -144,7 +144,7 @@ _cmd_add() { # Check if any Python-related profiles were added local python_profiles_added=false - for profile in "${selected[@]}"; do + for profile in "${selected[@]:-}"; do if [[ "$profile" == "python" ]] || [[ "$profile" == "ml" ]] || [[ "$profile" == "datascience" ]]; then python_profiles_added=true break @@ -162,7 +162,7 @@ _cmd_add() { # Only show rebuild message for non-Python profiles local needs_rebuild=false - for profile in "${selected[@]}"; do + for profile in "${selected[@]:-}"; do if [[ "$profile" != "python" ]] && [[ "$profile" != "ml" ]] && [[ "$profile" != "datascience" ]]; then needs_rebuild=true break @@ -175,7 +175,7 @@ _cmd_add() { echo if [[ ${#remaining[@]} -gt 0 ]]; then - set -- "${remaining[@]}" + set -- "${remaining[@]:-}" fi } @@ -231,7 +231,7 @@ _cmd_remove() { local python_profiles_removed=false for profile in "${current_profiles[@]}"; do local keep=true - for remove in "${to_remove[@]}"; do + for remove in "${to_remove[@]:-}"; do if [[ "$profile" == "$remove" ]]; then keep=false # Check if we're removing a Python-related profile @@ -246,7 +246,7 @@ _cmd_remove() { # Check if any Python-related profiles remain local has_python_profiles=false - for profile in "${new_profiles[@]}"; do + for profile in "${new_profiles[@]:-}"; do if [[ "$profile" == "python" ]] || [[ "$profile" == "ml" ]] || [[ "$profile" == "datascience" ]]; then has_python_profiles=true break @@ -275,7 +275,7 @@ _cmd_remove() { # Write back the filtered profiles { echo "[profiles]" - for profile in "${new_profiles[@]}"; do + for profile in "${new_profiles[@]:-}"; do echo "$profile" done echo "" diff --git a/lib/commands.system.sh b/lib/commands.system.sh index 75f7f7f..5f8402e 100644 --- a/lib/commands.system.sh +++ b/lib/commands.system.sh @@ -421,7 +421,7 @@ Current directory: $PWD" if [[ ${#window_panes[@]} -gt 0 ]]; then # Calculate total slots needed total_slots_needed=0 - for panes in "${window_panes[@]}"; do + for panes in "${window_panes[@]:-}"; do ((total_slots_needed += panes)) || true done @@ -558,7 +558,7 @@ Current directory: $PWD" local first_created=false # Create panes one by one, capturing IDs atomically - for panes in "${window_panes[@]}"; do + for panes in "${window_panes[@]:-}"; do for ((i=0; i $path" @@ -813,7 +813,7 @@ _cmd_import() { cecho "Available commands to import:" "$CYAN" echo local i=1 - for cmd in "${commands[@]}"; do + for cmd in "${commands[@]:-}"; do printf " %2d. %s\n" "$i" "$cmd" ((i++)) || true done @@ -832,7 +832,7 @@ _cmd_import() { a|A|all|ALL) # Import all commands local imported=0 - for cmd in "${commands[@]}"; do + for cmd in "${commands[@]:-}"; do if cp "$host_commands/$cmd" "$project_commands/"; then ((imported++)) || true fi diff --git a/lib/config.sh b/lib/config.sh index 640211a..48b5ef2 100755 --- a/lib/config.sh +++ b/lib/config.sh @@ -130,7 +130,7 @@ read_profile_section() { done < <(sed -n "/^\[$section\]/,/^\[/p" "$profile_file" | tail -n +2 | grep -v '^\[') fi - printf '%s\n' "${result[@]}" + printf '%s\n' "${result[@]:-}" } update_profile_section() { @@ -143,13 +143,13 @@ update_profile_section() { readarray -t existing_items < <(read_profile_section "$profile_file" "$section") local all_items=() - for item in "${existing_items[@]}"; do + for item in "${existing_items[@]:-}"; do [[ -n "$item" ]] && all_items+=("$item") done - for item in "${new_items[@]}"; do + for item in "${new_items[@]:-}"; do local found=false - for existing in "${all_items[@]}"; do + for existing in "${all_items[@]:-}"; do [[ "$existing" == "$item" ]] && found=true && break done [[ "$found" == "false" ]] && all_items+=("$item") @@ -169,7 +169,7 @@ update_profile_section() { fi echo "[$section]" - for item in "${all_items[@]}"; do + for item in "${all_items[@]:-}"; do echo "$item" done echo "" diff --git a/lib/docker.sh b/lib/docker.sh index 3e3fb50..49e4bb6 100755 --- a/lib/docker.sh +++ b/lib/docker.sh @@ -94,7 +94,7 @@ run_claudebox_container() { # Handle "attached" mode - start detached, wait, then attach if [[ "$run_mode" == "attached" ]]; then # Start detached - run_claudebox_container "$container_name" "detached" "${container_args[@]}" >/dev/null + run_claudebox_container "$container_name" "detached" "${container_args[@]:-}" >/dev/null # Show progress while container initializes fillbar @@ -398,14 +398,14 @@ run_claudebox_container() { # Add any additional arguments if [[ ${#container_args[@]} -gt 0 ]]; then - docker_args+=("${container_args[@]}") + docker_args+=("${container_args[@]:-}") fi # Run the container if [[ "$VERBOSE" == "true" ]]; then echo "[DEBUG] Docker run command: docker run ${docker_args[*]}" >&2 fi - docker run "${docker_args[@]}" + docker run "${docker_args[@]:-}" local exit_code=$? return $exit_code diff --git a/lib/tools-report.sh b/lib/tools-report.sh index df07621..391d1c3 100644 --- a/lib/tools-report.sh +++ b/lib/tools-report.sh @@ -31,7 +31,7 @@ generate_tools_report() { if [[ ${#profiles[@]} -gt 0 ]]; then echo "## Active Development Profiles" echo - for profile in "${profiles[@]}"; do + for profile in "${profiles[@]:-}"; do echo "### $profile" echo _describe_profile "$profile" @@ -66,7 +66,7 @@ generate_tools_report() { echo echo "The following custom packages have been installed:" echo - for package in "${packages[@]}"; do + for package in "${packages[@]:-}"; do echo "- $package" done echo diff --git a/main.sh b/main.sh index 0246f35..f4500d2 100755 --- a/main.sh +++ b/main.sh @@ -109,7 +109,7 @@ main() { if [[ ${#saved_flags[@]} -gt 0 ]]; then # Re-parse WITH saved flags, but the command structure is preserved # because the command was already identified from original args - parse_cli_args "${original_args[@]}" "${saved_flags[@]}" + parse_cli_args "${original_args[@]:-}" "${saved_flags[@]:-}" process_host_flags if [[ "$VERBOSE" == "true" ]]; then @@ -137,7 +137,7 @@ main() { # If command doesn't need Docker, skip all Docker setup if [[ "$cmd_requirements" == "none" ]]; then # Dispatch the command directly and exit - dispatch_command "${CLI_SCRIPT_COMMAND}" "${CLI_PASS_THROUGH[@]}" "${CLI_CONTROL_FLAGS[@]}" + dispatch_command "${CLI_SCRIPT_COMMAND}" "${CLI_PASS_THROUGH[@]:-}" "${CLI_CONTROL_FLAGS[@]:-}" exit $? fi @@ -299,7 +299,7 @@ main() { local cmd_req=$(get_command_requirements "${CLI_SCRIPT_COMMAND}") # Only run pre-flight for commands that need Docker or image if [[ "$cmd_req" == "docker" ]] || [[ "$cmd_req" == "image" ]]; then - if ! preflight_check "${CLI_SCRIPT_COMMAND}" "${CLI_PASS_THROUGH[@]}"; then + if ! preflight_check "${CLI_SCRIPT_COMMAND}" "${CLI_PASS_THROUGH[@]:-}"; then # Pre-flight check failed and printed error exit 1 fi @@ -353,9 +353,9 @@ main() { local docker_profiles=() local python_only_profiles=("python" "ml" "datascience") - for profile in "${current_profiles[@]}"; do + for profile in "${current_profiles[@]:-}"; do local is_python_only=false - for py_profile in "${python_only_profiles[@]}"; do + for py_profile in "${python_only_profiles[@]:-}"; do if [[ "$profile" == "$py_profile" ]]; then is_python_only=true break @@ -369,7 +369,7 @@ main() { # Calculate hash only for Docker-affecting profiles local docker_profiles_hash="" if [[ ${#docker_profiles[@]} -gt 0 ]]; then - docker_profiles_hash=$(printf '%s\n' "${docker_profiles[@]}" | sort | cksum | cut -d' ' -f1) + docker_profiles_hash=$(printf '%s\n' "${docker_profiles[@]:-}" | sort | cksum | cut -d' ' -f1) fi local image_profiles_hash=$(docker inspect "$IMAGE_NAME" --format '{{index .Config.Labels "claudebox.profiles"}}' 2>/dev/null || echo "") @@ -426,7 +426,7 @@ main() { if [[ -n "${CLI_SCRIPT_COMMAND}" ]]; then # Script command - dispatch on host # Pass control flags and pass-through args to dispatch_command - dispatch_command "${CLI_SCRIPT_COMMAND}" "${CLI_PASS_THROUGH[@]}" "${CLI_CONTROL_FLAGS[@]}" + dispatch_command "${CLI_SCRIPT_COMMAND}" "${CLI_PASS_THROUGH[@]:-}" "${CLI_CONTROL_FLAGS[@]:-}" exit $? else # No script command - running Claude interactively @@ -456,10 +456,10 @@ main() { # Re-parse all arguments with saved flags included if [[ ${#saved_flags[@]} -gt 0 ]]; then # Combine original args with saved flags - local all_args=("${original_args[@]}" "${saved_flags[@]}") + local all_args=("${original_args[@]:-}" "${saved_flags[@]:-}") # Re-parse to properly sort flags - parse_cli_args "${all_args[@]}" + parse_cli_args "${all_args[@]:-}" process_host_flags if [[ "$VERBOSE" == "true" ]]; then @@ -472,7 +472,7 @@ main() { # Check if stdin is not a terminal (i.e., we're receiving piped input) # and -p/--print flag isn't already present local has_print_flag=false - for arg in "${CLI_PASS_THROUGH[@]}"; do + for arg in "${CLI_PASS_THROUGH[@]:-}"; do if [[ "$arg" == "-p" ]] || [[ "$arg" == "--print" ]]; then has_print_flag=true break @@ -495,9 +495,9 @@ main() { fi local piped_input piped_input=$(cat) - run_claudebox_container "$container_name" "interactive" "${CLI_CONTROL_FLAGS[@]}" "-p" "$piped_input" "${CLI_PASS_THROUGH[@]}" + run_claudebox_container "$container_name" "interactive" "${CLI_CONTROL_FLAGS[@]:-}" "-p" "$piped_input" "${CLI_PASS_THROUGH[@]:-}" else - run_claudebox_container "$container_name" "interactive" "${CLI_CONTROL_FLAGS[@]}" "${CLI_PASS_THROUGH[@]}" + run_claudebox_container "$container_name" "interactive" "${CLI_CONTROL_FLAGS[@]:-}" "${CLI_PASS_THROUGH[@]:-}" fi else show_no_slots_menu @@ -537,7 +537,7 @@ build_docker_image() { done < <(read_profile_section "$profiles_file" "profiles") # Generate profile installations - for profile in "${current_profiles[@]}"; do + for profile in "${current_profiles[@]:-}"; do profile=$(echo "$profile" | tr -d '[:space:]') [[ -z "$profile" ]] && continue @@ -552,9 +552,9 @@ build_docker_image() { local docker_profiles=() local python_only_profiles=("python" "ml" "datascience") - for profile in "${current_profiles[@]}"; do + for profile in "${current_profiles[@]:-}"; do local is_python_only=false - for py_profile in "${python_only_profiles[@]}"; do + for py_profile in "${python_only_profiles[@]:-}"; do if [[ "$profile" == "$py_profile" ]]; then is_python_only=true break @@ -566,7 +566,7 @@ build_docker_image() { done if [[ ${#docker_profiles[@]} -gt 0 ]]; then - profile_hash=$(printf '%s\n' "${docker_profiles[@]}" | sort | cksum | cut -d' ' -f1) + profile_hash=$(printf '%s\n' "${docker_profiles[@]:-}" | sort | cksum | cut -d' ' -f1) fi fi diff --git a/tooling/profiles/rust.sh b/tooling/profiles/rust.sh index 0ef93ec..dd9ac1e 100644 --- a/tooling/profiles/rust.sh +++ b/tooling/profiles/rust.sh @@ -49,7 +49,7 @@ CARGO_TOOLS=( "hyperfine" ) -for tool in "${CARGO_TOOLS[@]}"; do +for tool in "${CARGO_TOOLS[@]:-}"; do if ! command -v "$tool" >/dev/null 2>&1; then echo "Installing $tool..." cargo install "$tool"