Skip to content
12 changes: 6 additions & 6 deletions lib/cli.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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[@]:+${all_args[@]}}"; do
if [[ " ${HOST_ONLY_FLAGS[*]} " == *" $arg "* ]]; then
# Bucket 1: Host-only flags
host_flags+=("$arg")
Expand All @@ -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[@]:+${host_flags[@]}}")
export CLI_CONTROL_FLAGS=("${control_flags[@]:+${control_flags[@]}}")
export CLI_SCRIPT_COMMAND="$script_command"
export CLI_PASS_THROUGH=("${pass_through[@]}")
export CLI_PASS_THROUGH=("${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[@]:+${CLI_HOST_FLAGS[@]}}"; do
case "$flag" in
--verbose)
export VERBOSE=true
Expand Down Expand Up @@ -134,4 +134,4 @@ debug_parsed_args() {
}

# Export all functions
export -f parse_cli_args process_host_flags get_command_requirements requires_docker_image requires_slot debug_parsed_args
export -f parse_cli_args process_host_flags get_command_requirements requires_docker_image requires_slot debug_parsed_args
8 changes: 4 additions & 4 deletions lib/commands.clean.sh
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,9 @@ _cmd_clean() {
done

# Handle results
if [ ${#matches[@]} -eq 0 ]; then
if [[ ${#matches[@]} -eq 0 ]]; then
error "No project found matching: $search"
elif [ ${#matches[@]} -eq 1 ]; then
elif [[ ${#matches[@]} -eq 1 ]]; then
# Single match - clean it
local project_info="${matches[0]}"
local project_path="${project_info%|*}"
Expand All @@ -101,7 +101,7 @@ _cmd_clean() {

if [[ "$choice" == "q" ]] || [[ -z "$choice" ]]; then
exit 0
elif [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -le "${#matches[@]}" ]; then
elif [[ "$choice" =~ ^[0-9]+$ ]] && [[ "$choice" -ge 1 ]] && [[ "$choice" -le "${#matches[@]}" ]]; then
local selected="${matches[$((choice-1))]}"
local project_path="${selected%|*}"
local project_name="${selected#*|}"
Expand Down Expand Up @@ -258,4 +258,4 @@ _cmd_redo() {
}

# Export functions
export -f _cmd_clean _clean_project _cmd_undo _cmd_redo
export -f _cmd_clean _clean_project _cmd_undo _cmd_redo
6 changes: 3 additions & 3 deletions lib/commands.core.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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[@]:+${shell_flags[@]}}"

# Commit changes back to image
fillbar
Expand All @@ -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[@]:+${shell_flags[@]}}"
fi

exit 0
Expand Down Expand Up @@ -230,4 +230,4 @@ _cmd_update() {
_cmd_special "update" "$@"
}

export -f _cmd_help _cmd_shell _cmd_update
export -f _cmd_help _cmd_shell _cmd_update
18 changes: 9 additions & 9 deletions lib/commands.profile.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ _cmd_profiles() {
local desc=$(get_profile_description "$profile")
local is_enabled=false
# Check if profile is currently enabled
for enabled in "${current_profiles[@]}"; do
for enabled in "${current_profiles[@]:-}"; do
if [[ "$enabled" == "$profile" ]]; then
is_enabled=true
break
Expand Down Expand Up @@ -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[@]:+${selected[@]}}"

local all_profiles=()
local all_profiles=()
Expand All @@ -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[@]:+${selected[@]}}"; do
if [[ "$profile" == "python" ]] || [[ "$profile" == "ml" ]] || [[ "$profile" == "datascience" ]]; then
python_profiles_added=true
break
Expand All @@ -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[@]:+${selected[@]}}"; do
if [[ "$profile" != "python" ]] && [[ "$profile" != "ml" ]] && [[ "$profile" != "datascience" ]]; then
needs_rebuild=true
break
Expand Down Expand Up @@ -229,9 +229,9 @@ _cmd_remove() {
# Remove specified profiles
local new_profiles=()
local python_profiles_removed=false
for profile in "${current_profiles[@]}"; do
for profile in "${current_profiles[@]:+${current_profiles[@]}}"; do
local keep=true
for remove in "${to_remove[@]}"; do
for remove in "${to_remove[@]:+${to_remove[@]}}"; do
if [[ "$profile" == "$remove" ]]; then
keep=false
# Check if we're removing a Python-related profile
Expand All @@ -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[@]:+${new_profiles[@]}}"; do
if [[ "$profile" == "python" ]] || [[ "$profile" == "ml" ]] || [[ "$profile" == "datascience" ]]; then
has_python_profiles=true
break
Expand Down Expand Up @@ -275,7 +275,7 @@ _cmd_remove() {
# Write back the filtered profiles
{
echo "[profiles]"
for profile in "${new_profiles[@]}"; do
for profile in "${new_profiles[@]:+${new_profiles[@]}}"; do
echo "$profile"
done
echo ""
Expand Down Expand Up @@ -323,4 +323,4 @@ _cmd_install() {
echo
}

export -f _cmd_profiles _cmd_profile _cmd_add _cmd_remove _cmd_install
export -f _cmd_profiles _cmd_profile _cmd_add _cmd_remove _cmd_install
24 changes: 12 additions & 12 deletions lib/commands.system.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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[@]:+${window_panes[@]}}"; do
((total_slots_needed += panes)) || true
done

Expand Down Expand Up @@ -519,10 +519,10 @@ Current directory: $PWD"

# Send activate command only to authenticated slots
if [[ "$VERBOSE" == "true" ]]; then
echo "[DEBUG] Checking authentication for ${#captured_panes[@]} panes" >&2
echo "[DEBUG] Checking authentication for ${#captured_panes[@]:-} panes" >&2
fi

for ((i=0; i<${#captured_panes[@]}; i++)); do
for ((i=0; i<${#captured_panes[@]:-0}; i++)); do
local pane_id="${captured_panes[$i]}"
local slot_num="${available_slots[$i]}"
local slot_name=$(generate_container_name "$PROJECT_DIR" "$slot_num")
Expand Down Expand Up @@ -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[@]:+${window_panes[@]}}"; do
for ((i=0; i<panes; i++)); do
if [[ $slot_index -ge ${#available_slots[@]} ]]; then
error "Not enough available slots for layout"
Expand Down Expand Up @@ -632,10 +632,10 @@ Current directory: $PWD"

# Send activate command only to authenticated slots
if [[ "$VERBOSE" == "true" ]]; then
echo "[DEBUG] Checking authentication for ${#captured_panes[@]} panes" >&2
echo "[DEBUG] Checking authentication for ${#captured_panes[@]:-} panes" >&2
fi

for ((i=0; i<${#captured_panes[@]}; i++)); do
for ((i=0; i<${#captured_panes[@]:-0}; i++)); do
local pane_id="${captured_panes[$i]}"
local slot_num="${available_slots[$i]}"
local slot_name=$(generate_container_name "$PROJECT_DIR" "$slot_num")
Expand Down Expand Up @@ -702,9 +702,9 @@ _cmd_project() {
done

# Handle results
if [ ${#matches[@]} -eq 0 ]; then
if [[ ${#matches[@]} -eq 0 ]]; then
error "No projects found matching '$search'"
elif [ ${#matches[@]} -eq 1 ]; then
elif [[ ${#matches[@]} -eq 1 ]]; then
# Single match - use it
local project_path="${matches[0]%%|*}"
local project_name="${matches[0]##*|}"
Expand All @@ -726,7 +726,7 @@ _cmd_project() {
else
# Multiple matches - show them
error "Multiple projects match '$search':"
for match in "${matches[@]}"; do
for match in "${matches[@]:-}"; do
local path="${match%%|*}"
local name="${match##*|}"
echo " $name -> $path"
Expand Down Expand Up @@ -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
Expand All @@ -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[@]:+${commands[@]}}"; do
if cp "$host_commands/$cmd" "$project_commands/"; then
((imported++)) || true
fi
Expand Down Expand Up @@ -938,4 +938,4 @@ _install_tmux_conf() {
fi
}

export -f _cmd_save _cmd_unlink _cmd_rebuild _cmd_tmux _cmd_project _cmd_special _cmd_import _install_tmux_conf _cmd_kill
export -f _cmd_save _cmd_unlink _cmd_rebuild _cmd_tmux _cmd_project _cmd_special _cmd_import _install_tmux_conf _cmd_kill
14 changes: 7 additions & 7 deletions lib/config.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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[@]:+${existing_items[@]}}"; do
[[ -n "$item" ]] && all_items+=("$item")
done

for item in "${new_items[@]}"; do
for item in "${new_items[@]:+${new_items[@]}}"; do
local found=false
for existing in "${all_items[@]}"; do
for existing in "${all_items[@]:+${all_items[@]}}"; do
[[ "$existing" == "$item" ]] && found=true && break
done
[[ "$found" == "false" ]] && all_items+=("$item")
Expand All @@ -169,7 +169,7 @@ update_profile_section() {
fi

echo "[$section]"
for item in "${all_items[@]}"; do
for item in "${all_items[@]:+${all_items[@]}}"; do
echo "$item"
done
echo ""
Expand All @@ -186,7 +186,7 @@ get_current_profiles() {
done < <(read_profile_section "$profiles_file" "profiles")
fi

printf '%s\n' "${current_profiles[@]}"
printf '%s\n' "${current_profiles[@]:-}"
}

# -------- Profile installation functions for Docker builds -------------------
Expand Down Expand Up @@ -370,4 +370,4 @@ export -f get_profile_file_path read_config_value read_profile_section update_pr
export -f get_profile_core get_profile_build_tools get_profile_shell get_profile_networking get_profile_c get_profile_openwrt
export -f get_profile_rust get_profile_python get_profile_go get_profile_flutter get_profile_javascript get_profile_java get_profile_ruby
export -f get_profile_php get_profile_database get_profile_devops get_profile_web get_profile_embedded get_profile_datascience
export -f get_profile_security get_profile_ml
export -f get_profile_security get_profile_ml
10 changes: 5 additions & 5 deletions lib/docker.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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[@]:+${container_args[@]}}" >/dev/null

# Show progress while container initializes
fillbar
Expand Down Expand Up @@ -287,16 +287,16 @@ run_claudebox_container() {
fi
}

local user_mcp_file=""
local project_mcp_file=""
user_mcp_file=""
project_mcp_file=""

# Track all temporary MCP files for cleanup
declare -a mcp_temp_files=()

# Set up cleanup trap for temporary MCP config files
cleanup_mcp_files() {
local file
for file in "${mcp_temp_files[@]}"; do
for file in "${mcp_temp_files[@]:+${mcp_temp_files[@]}}"; do
if [[ -f "$file" ]]; then
rm -f "$file"
fi
Expand Down Expand Up @@ -451,4 +451,4 @@ run_docker_build() {
-f "$1" -t "$IMAGE_NAME" "$2" || error "Docker build failed"
}

export -f check_docker install_docker configure_docker_nonroot docker_exec_root docker_exec_user run_claudebox_container check_container_exists run_docker_build
export -f check_docker install_docker configure_docker_nonroot docker_exec_root docker_exec_user run_claudebox_container check_container_exists run_docker_build
Loading