8000 feat: claude-code workspace persistence (#154) · coder/registry@e5ccf74 · GitHub
[go: up one dir, main page]

Skip to content

Commit e5ccf74

Browse files
feat: claude-code workspace persistence (#154)
## Description Add Tmux Plugin Manager with resurrect and continuum plugins. Add functionality to be able to enable workspace persistence to save the tmux session automatically so that it can persist through workspace restarts. --- ## Type of Change - [ ] New module - [ ] Bug fix - [X] Feature/enhancement - [ ] Documentation - [ ] Other --- ## Module Information **Path:** `registry/coder/modules/claude-code` **New version:** `v1.4.0` **Breaking change:** [ ] Yes [X] No --- ## Testing & Validation - [X] Tests pass (`bun test`) - [X] Code formatted (`bun run fmt`) - [X] Changes tested locally --- ## Related Issues Closes [#29](#29)
1 parent a47ff91 commit e5ccf74

File tree

2 files changed

+150
-28
lines changed

2 files changed

+150
-28
lines changed

registry/coder/modules/claude-code/README.md

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Run the [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude
1414
```tf
1515
module "claude-code" {
1616
source = "registry.coder.com/coder/claude-code/coder"
17-
version = "1.3.1"
17+
version = "1.4.0"
1818
agent_id = coder_agent.example.id
1919
folder = "/home/coder"
2020
install_claude_code = true
@@ -88,7 +88,7 @@ resource "coder_agent" "main" {
8888
module "claude-code" {
8989
count = data.coder_workspace.me.start_count
9090
source = "registry.coder.com/coder/claude-code/coder"
91-
version = "1.3.1"
91+
version = "1.4.0"
9292
agent_id = coder_agent.example.id
9393
folder = "/home/coder"
9494
install_claude_code = true
@@ -100,14 +100,37 @@ module "claude-code" {
100100
}
101101
```
102102

103+
## Session Persistence (Experimental)
104+
105+
Enable automatic session persistence to maintain Claude Code sessions across workspace restarts:
106+
107+
```tf
108+
module "claude-code" {
109+
count = data.coder_workspace.me.start_count
110+
source = "registry.coder.com/coder/claude-code/coder"
111+
version = "1.4.0"
112+
agent_id = coder_agent.example.id
113+
folder = "/home/coder"
114+
install_claude_code = true
115+
116+
# Enable tmux with session persistence
117+
experiment_use_tmux = true
118+
experiment_tmux_session_persistence = true
119+
experiment_tmux_session_save_interval = "10" # Save every 10 minutes
120+
experiment_report_tasks = true
121+
}
122+
```
123+
124+
Session persistence automatically saves and restores your Claude Code environment, including working directory and command history.
125+
103126
## Run standalone
104127

105128
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.
106129

107130
```tf
108131
module "claude-code" {
109132
source = "registry.coder.com/coder/claude-code/coder"
110-
version = "1.3.1"
133+
version = "1.4.0"
111134
agent_id = coder_agent.example.id
112135
folder = "/home/coder"
113136
install_claude_code = true

registry/coder/modules/claude-code/main.tf

Lines changed: 124 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
8799
locals {
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+
179B 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

Comments
 (0)
0