diff --git a/registry/coder/modules/claude-code/README.md b/registry/coder/modules/claude-code/README.md index 5056ce71..21ff81f2 100644 --- a/registry/coder/modules/claude-code/README.md +++ b/registry/coder/modules/claude-code/README.md @@ -14,7 +14,7 @@ Run the [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude ```tf module "claude-code" { source = "registry.coder.com/coder/claude-code/coder" - version = "1.3.1" + version = "1.4.0" agent_id = coder_agent.example.id folder = "/home/coder" install_claude_code = true @@ -88,7 +88,7 @@ resource "coder_agent" "main" { module "claude-code" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/claude-code/coder" - version = "1.3.1" + version = "1.4.0" agent_id = coder_agent.example.id folder = "/home/coder" install_claude_code = true @@ -100,6 +100,29 @@ module "claude-code" { } ``` +## Session Persistence (Experimental) + +Enable automatic session persistence to maintain Claude Code sessions across workspace restarts: + +```tf +module "claude-code" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/claude-code/coder" + version = "1.4.0" + agent_id = coder_agent.example.id + folder = "/home/coder" + install_claude_code = true + + # Enable tmux with session persistence + experiment_use_tmux = true + experiment_tmux_session_persistence = true + experiment_tmux_session_save_interval = "10" # Save every 10 minutes + experiment_report_tasks = true +} +``` + +Session persistence automatically saves and restores your Claude Code environment, including working directory and command history. + ## Run standalone Run Claude Code as a standalone app in your workspace. This will install Claude Code and run it directly without using screen or any task reporting to the Coder UI. @@ -107,7 +130,7 @@ Run Claude Code as a standalone app in your workspace. This will install Claude ```tf module "claude-code" { source = "registry.coder.com/coder/claude-code/coder" - version = "1.3.1" + version = "1.4.0" agent_id = coder_agent.example.id folder = "/home/coder" install_claude_code = true diff --git a/registry/coder/modules/claude-code/main.tf b/registry/coder/modules/claude-code/main.tf index 1435a2f3..d699b4f1 100644 --- a/registry/coder/modules/claude-code/main.tf +++ b/registry/coder/modules/claude-code/main.tf @@ -84,6 +84,18 @@ variable "experiment_post_install_script" { default = null } +variable "experiment_tmux_session_persistence" { + type = bool + description = "Whether to enable tmux session persistence across workspace restarts." + default = false +} + +variable "experiment_tmux_session_save_interval" { + type = string + description = "How often to save tmux sessions in minutes." + default = "15" +} + locals { encoded_pre_install_script = var.experiment_pre_install_script != null ? base64encode(var.experiment_pre_install_script) : "" encoded_post_install_script = var.experiment_post_install_script != null ? base64encode(var.experiment_post_install_script) : "" @@ -98,12 +110,28 @@ resource "coder_script" "claude_code" { #!/bin/bash set -e - # Function to check if a command exists command_exists() { command -v "$1" >/dev/null 2>&1 } - # Check if the specified folder exists + install_tmux() { + echo "Installing tmux..." + if command_exists apt-get; then + sudo apt-get update && sudo apt-get install -y tmux + elif command_exists yum; then + sudo yum install -y tmux + elif command_exists dnf; then + sudo dnf install -y tmux + elif command_exists pacman; then + sudo pacman -S --noconfirm tmux + elif command_exists apk; then + sudo apk add tmux + else + echo "Error: Unable to install tmux automatically. Package manager not recognized." + exit 1 + fi + } + if [ ! -d "${var.folder}" ]; then echo "Warning: The specified folder '${var.folder}' does not exist." echo "Creating the folder..." @@ -112,8 +140,6 @@ resource "coder_script" "claude_code" { mkdir -p "${var.folder}" echo "Folder created successfully." fi - - # Run pre-install script if provided if [ -n "${local.encoded_pre_install_script}" ]; then echo "Running pre-install script..." echo "${local.encoded_pre_install_script}" | base64 -d > /tmp/pre_install.sh @@ -121,11 +147,30 @@ resource "coder_script" "claude_code" { /tmp/pre_install.sh fi - # Install Claude Code if enabled if [ "${var.install_claude_code}" = "true" ]; then if ! command_exists npm; then - echo "Error: npm is not installed. Please install Node.js and npm first." - exit 1 + echo "npm not found, checking for Node.js installation..." + if ! command_exists node; then + echo "Node.js not found, installing Node.js via NVM..." + export NVM_DIR="$HOME/.nvm" + if [ ! -d "$NVM_DIR" ]; then + mkdir -p "$NVM_DIR" + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" + else + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" + fi + + nvm install --lts + nvm use --lts + nvm alias default node + + echo "Node.js installed: $(node --version)" + echo "npm installed: $(npm --version)" + else + echo "Node.js is installed but npm is not available. Please install npm manually." + exit 1 + fi fi echo "Installing Claude Code..." npm install -g @anthropic-ai/claude-code@${var.claude_code_version} @@ -136,7 +181,6 @@ resource "coder_script" "claude_code" { coder exp mcp configure claude-code ${var.folder} fi - # Run post-install script if provided if [ -n "${local.encoded_post_install_script}" ]; then echo "Running post-install script..." echo "${local.encoded_post_install_script}" | base64 -d > /tmp/post_install.sh @@ -144,46 +188,97 @@ resource "coder_script" "claude_code" { /tmp/post_install.sh fi - # Handle terminal multiplexer selection (tmux or screen) if [ "${var.experiment_use_tmux}" = "true" ] && [ "${var.experiment_use_screen}" = "true" ]; then echo "Error: Both experiment_use_tmux and experiment_use_screen cannot be true simultaneously." echo "Please set only one of them to true." exit 1 fi - # Run with tmux if enabled - if [ "${var.experiment_use_tmux}" = "true" ]; then - echo "Running Claude Code in the background with tmux..." + if [ "${var.experiment_tmux_session_persistence}" = "true" ] && [ "${var.experiment_use_tmux}" != "true" ]; then + echo "Error: Session persistence requires tmux to be enabled." + echo "Please set experiment_use_tmux = true when using session persistence." + exit 1 + fi - # Check if tmux is installed + if [ "${var.experiment_use_tmux}" = "true" ]; then if ! command_exists tmux; then - echo "Error: tmux is not installed. Please install tmux manually." - exit 1 + install_tmux fi - touch "$HOME/.claude-code.log" + if [ "${var.experiment_tmux_session_persistence}" = "true" ]; then + echo "Setting up tmux session persistence..." + if ! command_exists git; then + echo "Git not found, installing git..." + if command_exists apt-get; then + sudo apt-get update && sudo apt-get install -y git + elif command_exists yum; then + sudo yum install -y git + elif command_exists dnf; then + sudo dnf install -y git + elif command_exists pacman; then + sudo pacman -S --noconfirm git + elif command_exists apk; then + sudo apk add git + else + echo "Error: Unable to install git automatically. Package manager not recognized." + echo "Please install git manually to enable session persistence." + exit 1 + fi + fi + + mkdir -p ~/.tmux/plugins + if [ ! -d ~/.tmux/plugins/tpm ]; then + git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm + fi + + cat > ~/.tmux.conf << EOF +# Claude Code tmux persistence configuration +set -g @plugin 'tmux-plugins/tmux-resurrect' +set -g @plugin 'tmux-plugins/tmux-continuum' + +# Configure session persistence +set -g @resurrect-processes ':all:' +set -g @resurrect-capture-pane-contents 'on' +set -g @resurrect-save-bash-history 'on' +set -g @continuum-restore 'on' +set -g @continuum-save-interval '${var.experiment_tmux_session_save_interval}' +set -g @continuum-boot 'on' +set -g @continuum-save-on 'on' + +# Initialize plugin manager +run '~/.tmux/plugins/tpm/tpm' +EOF + + ~/.tmux/plugins/tpm/scripts/install_plugins.sh + fi + echo "Running Claude Code in the background with tmux..." + touch "$HOME/.claude-code.log" export LANG=en_US.UTF-8 export LC_ALL=en_US.UTF-8 - # Create a new tmux session in detached mode - tmux new-session -d -s claude-code -c ${var.folder} "claude --dangerously-skip-permissions \"$CODER_MCP_CLAUDE_TASK_PROMPT\"" - + if [ "${var.experiment_tmux_session_persistence}" = "true" ]; then + sleep 3 + + if ! tmux has-session -t claude-code 2>/dev/null; then + # Only create a new session if one doesn't exist + tmux new-session -d -s claude-code -c ${var.folder} "claude --dangerously-skip-permissions \"$CODER_MCP_CLAUDE_TASK_PROMPT\"" + fi + else + if ! tmux has-session -t claude-code 2>/dev/null; then + tmux new-session -d -s claude-code -c ${var.folder} "claude --dangerously-skip-permissions \"$CODER_MCP_CLAUDE_TASK_PROMPT\"" + fi + fi fi - # Run with screen if enabled if [ "${var.experiment_use_screen}" = "true" ]; then echo "Running Claude Code in the background..." - - # Check if screen is installed if ! command_exists screen; then echo "Error: screen is not installed. Please install screen manually." exit 1 fi touch "$HOME/.claude-code.log" - - # Ensure the screenrc exists if [ ! -f "$HOME/.screenrc" ]; then echo "Creating ~/.screenrc and adding multiuser settings..." | tee -a "$HOME/.claude-code.log" echo -e "multiuser on\nacladd $(whoami)" > "$HOME/.screenrc" @@ -198,6 +293,7 @@ resource "coder_script" "claude_code" { echo "Adding 'acladd $(whoami)' to ~/.screenrc..." | tee -a "$HOME/.claude-code.log" echo "acladd $(whoami)" >> "$HOME/.screenrc" fi + export LANG=en_US.UTF-8 export LC_ALL=en_US.UTF-8 @@ -207,7 +303,6 @@ resource "coder_script" "claude_code" { exec bash ' else - # Check if claude is installed before running if ! command_exists claude; then echo "Error: Claude Code is not installed. Please enable install_claude_code or install it manually." exit 1 @@ -231,6 +326,10 @@ resource "coder_app" "claude_code" { if [ "${var.experiment_use_tmux}" = "true" ]; then if tmux has-session -t claude-code 2>/dev/null; then echo "Attaching to existing Claude Code tmux session." | tee -a "$HOME/.claude-code.log" + # If Claude isn't running in the session, start it without the prompt + if ! tmux list-panes -t claude-code -F '#{pane_current_command}' | grep -q "claude"; then + tmux send-keys -t claude-code "cd ${var.folder} && claude -c --dangerously-skip-permissions" C-m + fi tmux attach-session -t claude-code else echo "Starting a new Claude Code tmux session." | tee -a "$HOME/.claude-code.log"