Skip to content

Commit 8c2ea35

Browse files
authored
feat: add preinstall/postinstall script + tools for goose and claude code (#424)
Co-authored-by: = <=>
1 parent d4b4ebd commit 8c2ea35

File tree

3 files changed

+179
-34
lines changed

3 files changed

+179
-34
lines changed

Diff for: claude-code/main.tf

+33
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,23 @@ variable "experiment_report_tasks" {
6060
default = false
6161
}
6262

63+
variable "experiment_pre_install_script" {
64+
type = string
65+
description = "Custom script to run before installing Claude Code."
66+
default = null
67+
}
68+
69+
variable "experiment_post_install_script" {
70+
type = string
71+
description = "Custom script to run after installing Claude Code."
72+
default = null
73+
}
74+
75+
locals {
76+
encoded_pre_install_script = var.experiment_pre_install_script != null ? base64encode(var.experiment_pre_install_script) : ""
77+
encoded_post_install_script = var.experiment_post_install_script != null ? base64encode(var.experiment_post_install_script) : ""
78+
}
79+
6380
# Install and Initialize Claude Code
6481
resource "coder_script" "claude_code" {
6582
agent_id = var.agent_id
@@ -74,6 +91,14 @@ resource "coder_script" "claude_code" {
7491
command -v "$1" >/dev/null 2>&1
7592
}
7693
94+
# Run pre-install script if provided
95+
if [ -n "${local.encoded_pre_install_script}" ]; then
96+
echo "Running pre-install script..."
97+
echo "${local.encoded_pre_install_script}" | base64 -d > /tmp/pre_install.sh
98+
chmod +x /tmp/pre_install.sh
99+
/tmp/pre_install.sh
100+
fi
101+
77102
# Install Claude Code if enabled
78103
if [ "${var.install_claude_code}" = "true" ]; then
79104
if ! command_exists npm; then
@@ -84,6 +109,14 @@ resource "coder_script" "claude_code" {
84109
npm install -g @anthropic-ai/claude-code@${var.claude_code_version}
85110
fi
86111
112+
# Run post-install script if provided
113+
if [ -n "${local.encoded_post_install_script}" ]; then
114+
echo "Running post-install script..."
115+
echo "${local.encoded_post_install_script}" | base64 -d > /tmp/post_install.sh
116+
chmod +x /tmp/post_install.sh
117+
/tmp/post_install.sh
118+
fi
119+
87120
if [ "${var.experiment_report_tasks}" = "true" ]; then
88121
echo "Configuring Claude Code to report tasks via Coder MCP..."
89122
coder exp mcp configure claude-code ${var.folder}

Diff for: goose/README.md

+30
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,36 @@ module "goose" {
111111
}
112112
```
113113

114+
### Adding Custom Extensions (MCP)
115+
116+
You can extend Goose's capabilities by adding custom extensions. For example, to add the desktop-commander extension:
117+
118+
```tf
119+
module "goose" {
120+
# ... other configuration ...
121+
122+
experiment_pre_install_script = <<-EOT
123+
npm i -g @wonderwhy-er/desktop-commander@latest
124+
EOT
125+
126+
experiment_additional_extensions = <<-EOT
127+
desktop-commander:
128+
args: []
129+
cmd: desktop-commander
130+
description: Ideal for background tasks
131+
enabled: true
132+
envs: {}
133+
name: desktop-commander
134+
timeout: 300
135+
type: stdio
136+
EOT
137+
}
138+
```
139+
140+
This will add the desktop-commander extension to Goose, allowing it to run commands in the background. The extension will be available in the Goose interface and can be used to run long-running processes like development servers.
141+
142+
Note: The indentation in the heredoc is preserved, so you can write the YAML naturally.
143+
114144
## Run standalone
115145

116146
Run Goose as a standalone app in your workspace. This will install Goose and run it directly without using screen or any task reporting to the Coder UI.

Diff for: goose/main.tf

+116-34
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,60 @@ variable "experiment_goose_model" {
7878
default = null
7979
}
8080

81+
variable "experiment_pre_install_script" {
82+
type = string
83+
description = "Custom script to run before installing Goose."
84+
default = null
85+
}
86+
87+
variable "experiment_post_install_script" {
88+
type = string
89+
description = "Custom script to run after installing Goose."
90+
default = null
91+
}
92+
93+
variable "experiment_additional_extensions" {
94+
type = string
95+
description = "Additional extensions configuration in YAML format to append to the config."
96+
default = null
97+
}
98+
99+
locals {
100+
base_extensions = <<-EOT
101+
coder:
102+
args:
103+
- exp
104+
- mcp
105+
- server
106+
cmd: coder
107+
description: Report ALL tasks and statuses (in progress, done, failed) you are working on.
108+
enabled: true
109+
envs:
110+
CODER_MCP_APP_STATUS_SLUG: goose
111+
name: Coder
112+
timeout: 3000
113+
type: stdio
114+
developer:
115+
display_name: Developer
116+
enabled: true
117+
name: developer
118+
timeout: 300
119+
type: builtin
120+
EOT
121+
122+
# Add two spaces to each line of extensions to match YAML structure
123+
formatted_base = " ${replace(trimspace(local.base_extensions), "\n", "\n ")}"
124+
additional_extensions = var.experiment_additional_extensions != null ? "\n ${replace(trimspace(var.experiment_additional_extensions), "\n", "\n ")}" : ""
125+
126+
combined_extensions = <<-EOT
127+
extensions:
128+
${local.formatted_base}${local.additional_extensions}
129+
EOT
130+
131+
encoded_pre_install_script = var.experiment_pre_install_script != null ? base64encode(var.experiment_pre_install_script) : ""
132+
encoded_post_install_script = var.experiment_post_install_script != null ? base64encode(var.experiment_post_install_script) : ""
133+
}
134+
81135
# Install and Initialize Goose
82136
resource "coder_script" "goose" {
83137
agent_id = var.agent_id
@@ -92,6 +146,14 @@ resource "coder_script" "goose" {
92146
command -v "$1" >/dev/null 2>&1
93147
}
94148
149+
# Run pre-install script if provided
150+
if [ -n "${local.encoded_pre_install_script}" ]; then
151+
echo "Running pre-install script..."
152+
echo "${local.encoded_pre_install_script}" | base64 -d > /tmp/pre_install.sh
153+
chmod +x /tmp/pre_install.sh
154+
/tmp/pre_install.sh
155+
fi
156+
95157
# Install Goose if enabled
96158
if [ "${var.install_goose}" = "true" ]; then
97159
if ! command_exists npm; then
@@ -102,36 +164,29 @@ resource "coder_script" "goose" {
102164
RELEASE_TAG=v${var.goose_version} curl -fsSL https://github.com/block/goose/releases/download/stable/download_cli.sh | CONFIGURE=false bash
103165
fi
104166
167+
# Run post-install script if provided
168+
if [ -n "${local.encoded_post_install_script}" ]; then
169+
echo "Running post-install script..."
170+
echo "${local.encoded_post_install_script}" | base64 -d > /tmp/post_install.sh
171+
chmod +x /tmp/post_install.sh
172+
/tmp/post_install.sh
173+
fi
174+
105175
# Configure Goose if auto-configure is enabled
106176
if [ "${var.experiment_auto_configure}" = "true" ]; then
107177
echo "Configuring Goose..."
108178
mkdir -p "$HOME/.config/goose"
109179
cat > "$HOME/.config/goose/config.yaml" << EOL
110180
GOOSE_PROVIDER: ${var.experiment_goose_provider}
111181
GOOSE_MODEL: ${var.experiment_goose_model}
112-
extensions:
113-
coder:
114-
args:
115-
- exp
116-
- mcp
117-
- server
118-
cmd: coder
119-
description: Report ALL tasks and statuses (in progress, done, failed) before and after starting
120-
enabled: true
121-
envs:
122-
CODER_MCP_APP_STATUS_SLUG: goose
123-
name: Coder
124-
timeout: 3000
125-
type: stdio
126-
developer:
127-
display_name: Developer
128-
enabled: true
129-
name: developer
130-
timeout: 300
131-
type: builtin
182+
${trimspace(local.combined_extensions)}
132183
EOL
133184
fi
134185
186+
# Write system prompt to config
187+
mkdir -p "$HOME/.config/goose"
188+
echo "$GOOSE_SYSTEM_PROMPT" > "$HOME/.config/goose/.goosehints"
189+
135190
# Run with screen if enabled
136191
if [ "${var.experiment_use_screen}" = "true" ]; then
137192
echo "Running Goose in the background..."
@@ -162,14 +217,28 @@ EOL
162217
export LANG=en_US.UTF-8
163218
export LC_ALL=en_US.UTF-8
164219
165-
screen -U -dmS goose bash -c '
220+
# Determine goose command
221+
if command_exists goose; then
222+
GOOSE_CMD=goose
223+
elif [ -f "$HOME/.local/bin/goose" ]; then
224+
GOOSE_CMD="$HOME/.local/bin/goose"
225+
else
226+
echo "Error: Goose is not installed. Please enable install_goose or install it manually."
227+
exit 1
228+
fi
229+
230+
screen -U -dmS goose bash -c "
166231
cd ${var.folder}
167-
$HOME/.local/bin/goose run --text "$GOOSE_SYSTEM_PROMPT. Your task: $GOOSE_TASK_PROMPT" --interactive | tee -a "$HOME/.goose.log"
168-
exec bash
169-
'
232+
\"$GOOSE_CMD\" run --text \"Review your goosehints. Every step of the way, report tasks to Coder with proper descriptions and statuses. Your task at hand: $GOOSE_TASK_PROMPT\" --interactive | tee -a \"$HOME/.goose.log\"
233+
/bin/bash
234+
"
170235
else
171236
# Check if goose is installed before running
172-
if ! command_exists $HOME/.local/bin/goose; then
237+
if command_exists goose; then
238+
GOOSE_CMD=goose
239+
elif [ -f "$HOME/.local/bin/goose" ]; then
240+
GOOSE_CMD="$HOME/.local/bin/goose"
241+
else
173242
echo "Error: Goose is not installed. Please enable install_goose or install it manually."
174243
exit 1
175244
fi
@@ -186,21 +255,34 @@ resource "coder_app" "goose" {
186255
#!/bin/bash
187256
set -e
188257
258+
# Function to check if a command exists
259+
command_exists() {
260+
command -v "$1" >/dev/null 2>&1
261+
}
262+
263+
# Determine goose command
264+
if command_exists goose; then
265+
GOOSE_CMD=goose
266+
elif [ -f "$HOME/.local/bin/goose" ]; then
267+
GOOSE_CMD="$HOME/.local/bin/goose"
268+
else
269+
echo "Error: Goose is not installed. Please enable install_goose or install it manually."
270+
exit 1
271+
fi
272+
189273
if [ "${var.experiment_use_screen}" = "true" ]; then
190-
if screen -list | grep -q "goose"; then
191-
export LANG=en_US.UTF-8
192-
export LC_ALL=en_US.UTF-8
193-
echo "Attaching to existing Goose session." | tee -a "$HOME/.goose.log"
194-
screen -xRR goose
195-
else
196-
echo "Starting a new Goose session." | tee -a "$HOME/.goose.log"
197-
screen -S goose bash -c 'export LANG=en_US.UTF-8; export LC_ALL=en_US.UTF-8; $HOME/.local/bin/goose run --text "Always report status and instructions to Coder, before and after your steps" --interactive | tee -a "$HOME/.goose.log"; exec bash'
274+
# Check if session exists first
275+
if ! screen -list | grep -q "goose"; then
276+
echo "Error: No existing Goose session found. Please wait for the script to start it."
277+
exit 1
198278
fi
279+
# Only attach to existing session
280+
screen -xRR goose
199281
else
200282
cd ${var.folder}
201283
export LANG=en_US.UTF-8
202284
export LC_ALL=en_US.UTF-8
203-
$HOME/.local/bin/goose
285+
"$GOOSE_CMD" run --text "Review goosehints. Your task: $GOOSE_TASK_PROMPT" --interactive
204286
fi
205287
EOT
206288
icon = var.icon

0 commit comments

Comments
 (0)