#!/bin/bash set -euo pipefail # Claude Code Devcontainer CLI Helper # Provides the `devc` command for managing devcontainers SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_NAME="$(basename "$0")" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color print_usage() { cat < [options] Commands: . Install devcontainer template to current directory and start up Start the devcontainer in current directory rebuild Rebuild the devcontainer (preserves auth volumes) down Stop the devcontainer shell Open a shell in the running container self-install Install 'devc' command to ~/.local/bin template [dir] Copy devcontainer template to directory (default: current) help Show this help message Examples: devc . # Install template and start container devc up # Start container in current directory devc rebuild # Clean rebuild devc shell # Open interactive shell devc self-install # Install devc to PATH EOF } log_info() { echo -e "${BLUE}[devc]${NC} $1" } log_success() { echo -e "${GREEN}[devc]${NC} $1" } log_warn() { echo -e "${YELLOW}[devc]${NC} $1" } log_error() { echo -e "${RED}[devc]${NC} $1" >&2 } check_devcontainer_cli() { if ! command -v devcontainer &>/dev/null; then log_error "devcontainer CLI not found." log_info "Install it with: npm install -g @devcontainers/cli" exit 1 fi } get_workspace_folder() { echo "${1:-$(pwd)}" } cmd_template() { local target_dir="${1:-.}" target_dir="$(cd "$target_dir" 2>/dev/null && pwd)" || { log_error "Directory does not exist: $1" exit 1 } local devcontainer_dir="$target_dir/.devcontainer" if [[ -d "$devcontainer_dir" ]]; then log_warn "Devcontainer already exists at $devcontainer_dir" read -p "Overwrite? [y/N] " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then log_info "Aborted." exit 0 fi fi mkdir -p "$devcontainer_dir" # Copy template files cp "$SCRIPT_DIR/Dockerfile" "$devcontainer_dir/" cp "$SCRIPT_DIR/devcontainer.json" "$devcontainer_dir/" cp "$SCRIPT_DIR/post_install.py" "$devcontainer_dir/" log_success "Template installed to $devcontainer_dir" } cmd_up() { local workspace_folder workspace_folder="$(get_workspace_folder "$1")" check_devcontainer_cli log_info "Starting devcontainer in $workspace_folder..." devcontainer up --workspace-folder "$workspace_folder" log_success "Devcontainer started" } cmd_rebuild() { local workspace_folder workspace_folder="$(get_workspace_folder "$1")" check_devcontainer_cli log_info "Rebuilding devcontainer in $workspace_folder..." devcontainer up --workspace-folder "$workspace_folder" --remove-existing-container log_success "Devcontainer rebuilt" } cmd_down() { local workspace_folder workspace_folder="$(get_workspace_folder "$1")" check_devcontainer_cli log_info "Stopping devcontainer..." # Get container ID and stop it local container_id container_id=$(docker ps -q --filter "label=devcontainer.local_folder=$workspace_folder" 2>/dev/null || true) if [[ -n "$container_id" ]]; then docker stop "$container_id" log_success "Devcontainer stopped" else log_warn "No running devcontainer found for $workspace_folder" fi } cmd_shell() { local workspace_folder workspace_folder="$(get_workspace_folder)" check_devcontainer_cli log_info "Opening shell in devcontainer..." devcontainer exec --workspace-folder "$workspace_folder" zsh } cmd_self_install() { local install_dir="$HOME/.local/bin" local install_path="$install_dir/devc" mkdir -p "$install_dir" # Create a symlink to the original script ln -sf "$SCRIPT_DIR/$SCRIPT_NAME" "$install_path" log_success "Installed 'devc' to $install_path" # Check if in PATH if [[ ":$PATH:" != *":$install_dir:"* ]]; then log_warn "$install_dir is not in your PATH" log_info "Add this to your shell profile:" echo " export PATH=\"\$HOME/.local/bin:\$PATH\"" fi } cmd_dot() { # Install template and start container in one command cmd_template "." cmd_up "." } # Main command dispatcher main() { if [[ $# -eq 0 ]]; then print_usage exit 1 fi local command="$1" shift case "$command" in .) cmd_dot ;; up) cmd_up "$@" ;; rebuild) cmd_rebuild "$@" ;; down) cmd_down "$@" ;; shell) cmd_shell ;; self-install) cmd_self_install ;; template) cmd_template "$@" ;; help | --help | -h) print_usage ;; *) log_error "Unknown command: $command" print_usage exit 1 ;; esac } main "$@"