@@ -84,6 +84,18 @@ variable "experiment_post_install_script" {
8484 default = null
8585}
8686
87+ variable "experiment_tmux_session_persistence" {
88+ type = bool
89+ description = " Whether to enable tmux session persistence across workspace restarts."
90+ default = false
91+ }
92+
93+ variable "experiment_tmux_session_save_interval" {
94+ type = string
95+ description = " How often to save tmux sessions in minutes."
96+ default = " 15"
97+ }
98+
8799locals {
88100 encoded_pre_install_script = var. experiment_pre_install_script != null ? base64encode (var. experiment_pre_install_script ) : " "
89101 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" {
98110 #!/bin/bash
99111 set -e
100112
101- # Function to check if a command exists
102113 command_exists() {
103114 command -v "$1" >/dev/null 2>&1
104115 }
105116
106- # Check if the specified folder exists
117+ install_tmux() {
118+ echo "Installing tmux..."
119+ if command_exists apt-get; then
120+ sudo apt-get update && sudo apt-get install -y tmux
121+ elif command_exists yum; then
122+ sudo yum install -y tmux
123+ elif command_exists dnf; then
124+ sudo dnf install -y tmux
125+ elif command_exists pacman; then
126+ sudo pacman -S --noconfirm tmux
127+ elif command_exists apk; then
128+ sudo apk add tmux
129+ else
130+ echo "Error: Unable to install tmux automatically. Package manager not recognized."
131+ exit 1
132+ fi
133+ }
134+
107135 if [ ! -d "${ var . folder } " ]; then
108136 echo "Warning: The specified folder '${ var . folder } ' does not exist."
109137 echo "Creating the folder..."
@@ -112,20 +140,37 @@ resource "coder_script" "claude_code" {
112140 mkdir -p "${ var . folder } "
113141 echo "Folder created successfully."
114142 fi
115-
116- # Run pre-install script if provided
117143 if [ -n "${ local . encoded_pre_install_script } " ]; then
118144 echo "Running pre-install script..."
119145 echo "${ local . encoded_pre_install_script } " | base64 -d > /tmp/pre_install.sh
120146 chmod +x /tmp/pre_install.sh
121147 /tmp/pre_install.sh
122148 fi
123149
124- # Install Claude Code if enabled
125150 if [ "${ var . install_claude_code } " = "true" ]; then
126151 if ! command_exists npm; then
127- echo "Error: npm is not installed. Please install Node.js and npm first."
128- exit 1
152+ echo "npm not found, checking for Node.js installation..."
153+ if ! command_exists node; then
154+ echo "Node.js not found, installing Node.js via NVM..."
155+ export NVM_DIR="$HOME/.nvm"
156+ if [ ! -d "$NVM_DIR" ]; then
157+ mkdir -p "$NVM_DIR"
158+ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
159+ [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
160+ else
161+ [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
162+ fi
163+
164+ nvm install --lts
165+ nvm use --lts
166+ nvm alias default node
167+
168+ echo "Node.js installed: $(node --version)"
169+ echo "npm installed: $(npm --version)"
170+ else
171+ echo "Node.js is installed but npm is not available. Please install npm manually."
172+ exit 1
173+ fi
129174 fi
130175 echo "Installing Claude Code..."
131176 npm install -g @anthropic-ai/claude-code@${ var . claude_code_version }
@@ -136,54 +181,104 @@ resource "coder_script" "claude_code" {
136181 coder exp mcp configure claude-code ${ var . folder }
137182 fi
138183
139- # Run post-install script if provided
140184 if [ -n "${ local . encoded_post_install_script } " ]; then
141185 echo "Running post-install script..."
142186 echo "${ local . encoded_post_install_script } " | base64 -d > /tmp/post_install.sh
143187 chmod +x /tmp/post_install.sh
144188 /tmp/post_install.sh
145189 fi
146190
147- # Handle terminal multiplexer selection (tmux or screen)
148191 if [ "${ var . experiment_use_tmux } " = "true" ] && [ "${ var . experiment_use_screen } " = "true" ]; then
149192 echo "Error: Both experiment_use_tmux and experiment_use_screen cannot be true simultaneously."
150193 echo "Please set only one of them to true."
151194 exit 1
152195 fi
153196
154- # Run with tmux if enabled
155- if [ "${ var . experiment_use_tmux } " = "true" ]; then
156- echo "Running Claude Code in the background with tmux..."
197+ if [ "${ var . experiment_tmux_session_persistence } " = "true" ] && [ "${ var . experiment_use_tmux } " != "true" ]; then
198+ echo "Error: Session persistence requires tmux to be enabled."
199+ echo "Please set experiment_use_tmux = true when using session persistence."
200+ exit 1
201+ fi
157202
158- # Check if tmux is installed
203+ if [ " ${ var . experiment_use_tmux } " = "true" ]; then
159204 if ! command_exists tmux; then
160- echo "Error: tmux is not installed. Please install tmux manually."
161- exit 1
205+ install_tmux
162206 fi
163207
164- touch "$HOME/.claude-code.log"
208+ if [ "${ var . experiment_tmux_session_persistence } " = "true" ]; then
209+ echo "Setting up tmux session persistence..."
210+ if ! command_exists git; then
211+ echo "Git not found, installing git..."
212+ if command_exists apt-get; then
213+ sudo apt-get update && sudo apt-get install -y git
214+ elif command_exists yum; then
215+ sudo yum install -y git
216+ elif command_exists dnf; then
217+ sudo dnf install -y git
218+ elif command_exists pacman; then
219+ sudo pacman -S --noconfirm git
220+ elif command_exists apk; then
221+ sudo apk add git
222+ else
223+ echo "Error: Unable to install git automatically. Package manager not recognized."
224+ echo "Please install git manually to enable session persistence."
225+ exit 1
226+ fi
227+ fi
228+
229+ mkdir -p ~/.tmux/plugins
230+ if [ ! -d ~/.tmux/plugins/tpm ]; then
231+ git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm
232+ fi
233+
234+ cat > ~/.tmux.conf << EOF
235+ # Claude Code tmux persistence configuration
236+ set -g @plugin 'tmux-plugins/tmux-resurrect'
237+ set -g @plugin 'tmux-plugins/tmux-continuum'
238+
239+ # Configure session persistence
240+ set -g @resurrect-processes ':all:'
241+ set -g @resurrect-capture-pane-contents 'on'
242+ set -g @resurrect-save-bash-history 'on'
243+ set -g @continuum-restore 'on'
244+ set -g @continuum-save-interval '${ var . experiment_tmux_session_save_interval } '
245+ set -g @continuum-boot 'on'
246+ set -g @continuum-save-on 'on'
247+
248+ # Initialize plugin manager
249+ run '~/.tmux/plugins/tpm/tpm'
250+ EOF
251+
252+ ~/.tmux/plugins/tpm/scripts/install_plugins.sh
253+ fi
165254
255+ echo "Running Claude Code in the background with tmux..."
256+ touch "$HOME/.claude-code.log"
166257 export LANG=en_US.UTF-8
167258 export LC_ALL=en_US.UTF-8
168259
169- # Create a new tmux session in detached mode
170- tmux new-session -d -s claude-code -c ${ var . folder } "claude --dangerously-skip-permissions \"$CODER_MCP_CLAUDE_TASK_PROMPT\""
171-
260+ if [ "${ var . experiment_tmux_session_persistence } " = "true" ]; then
261+ sleep 3
262+
263+ if ! tmux has-session -t claude-code 2>/dev/null; then
264+ # Only create a new session if one doesn't exist
265+ tmux new-session -d -s claude-code -c ${ var . folder } "claude --dangerously-skip-permissions \"$CODER_MCP_CLAUDE_TASK_PROMPT\""
266+ fi
267+ else
268+ if ! tmux has-session -t claude-code 2>/dev/null; then
269+ tmux new-session -d -s claude-code -c ${ var . folder } "claude --dangerously-skip-permissions \"$CODER_MCP_CLAUDE_TASK_PROMPT\""
270+ fi
271+ fi
172272 fi
173273
174- # Run with screen if enabled
175274 if [ "${ var . experiment_use_screen } " = "true" ]; then
176275 echo "Running Claude Code in the background..."
177-
178- # Check if screen is installed
179276 if ! command_exists screen; then
180277 echo "Error: screen is not installed. Please install screen manually."
181278 exit 1
182279 fi
183280
184281 touch "$HOME/.claude-code.log"
185-
186- # Ensure the screenrc exists
187282 if [ ! -f "$HOME/.screenrc" ]; then
188283 echo "Creating ~/.screenrc and adding multiuser settings..." | tee -a "$HOME/.claude-code.log"
189284 echo -e "multiuser on\nacladd $(whoami)" > "$HOME/.screenrc"
@@ -198,6 +293,7 @@ resource "coder_script" "claude_code" {
198293 echo "Adding 'acladd $(whoami)' to ~/.screenrc..." | tee -a "$HOME/.claude-code.log"
199294 echo "acladd $(whoami)" >> "$HOME/.screenrc"
200295 fi
296+
201297 export LANG=en_US.UTF-8
202298 export LC_ALL=en_US.UTF-8
203299
@@ -207,7 +303,6 @@ resource "coder_script" "claude_code" {
207303 exec bash
208304 '
209305 else
210- # Check if claude is installed before running
211306 if ! command_exists claude; then
212307 echo "Error: Claude Code is not installed. Please enable install_claude_code or install it manually."
213308 exit 1
@@ -231,6 +326,10 @@ resource "coder_app" "claude_code" {
231326 if [ "${ var . experiment_use_tmux } " = "true" ]; then
232327 if tmux has-session -t claude-code 2>/dev/null; then
233328 echo "Attaching to existing Claude Code tmux session." | tee -a "$HOME/.claude-code.log"
329+ # If Claude isn't running in the session, start it without the prompt
330+ if ! tmux list-panes -t claude-code -F '#{pane_current_command}' | grep -q "claude"; then
331+ tmux send-keys -t claude-code "cd ${ var . folder } && claude -c --dangerously-skip-permissions" C-m
332+ fi
234333 tmux attach-session -t claude-code
235334 else
236335 echo "Starting a new Claude Code tmux session." | tee -a "$HOME/.claude-code.log"
0 commit comments