Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions registry/coder/modules/claude-code/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,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 = "3.4.4"
version = "3.4.5"
agent_id = coder_agent.example.id
workdir = "/home/coder/project"
claude_api_key = "xxxx-xxxxx-xxxx"
Expand Down Expand Up @@ -70,7 +70,7 @@ data "coder_parameter" "ai_prompt" {

module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "3.4.4"
version = "3.4.5"
agent_id = coder_agent.example.id
workdir = "/home/coder/project"

Expand Down Expand Up @@ -106,7 +106,7 @@ Run and configure Claude Code as a standalone CLI in your workspace.
```tf
module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "3.4.4"
version = "3.4.5"
agent_id = coder_agent.example.id
workdir = "/home/coder"
install_claude_code = true
Expand All @@ -129,7 +129,7 @@ variable "claude_code_oauth_token" {

module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "3.4.4"
version = "3.4.5"
agent_id = coder_agent.example.id
workdir = "/home/coder/project"
claude_code_oauth_token = var.claude_code_oauth_token
Expand Down Expand Up @@ -202,7 +202,7 @@ resource "coder_env" "bedrock_api_key" {

module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "3.4.4"
version = "3.4.5"
agent_id = coder_agent.example.id
workdir = "/home/coder/project"
model = "global.anthropic.claude-sonnet-4-5-20250929-v1:0"
Expand Down Expand Up @@ -259,7 +259,7 @@ resource "coder_env" "google_application_credentials" {

module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "3.4.4"
version = "3.4.5"
agent_id = coder_agent.example.id
workdir = "/home/coder/project"
model = "claude-sonnet-4@20250514"
Expand Down
42 changes: 40 additions & 2 deletions registry/coder/modules/claude-code/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,15 +198,16 @@ describe("claude-code", async () => {
expect(startLog.stdout).toContain(`--model ${model}`);
});

test("claude-continue-resume-existing-session", async () => {
test("claude-continue-resume-task-session", async () => {
const { id } = await setup({
moduleVariables: {
continue: "true",
report_tasks: "true",
ai_prompt: "test prompt",
},
});

// Create a mock session file with the predefined task session ID
// Create a mock task session file with the hardcoded task session ID
const taskSessionId = "cd32e253-ca16-4fd3-9825-d837e74ae3c2";
const sessionDir = `/home/coder/.claude/projects/-home-coder-project`;
await execContainer(id, ["mkdir", "-p", sessionDir]);
Expand All @@ -226,6 +227,43 @@ describe("claude-code", async () => {
expect(startLog.stdout).toContain("--resume");
expect(startLog.stdout).toContain(taskSessionId);
expect(startLog.stdout).toContain("Resuming existing task session");
expect(startLog.stdout).toContain("--dangerously-skip-permissions");
});

test("claude-continue-resume-standalone-session", async () => {
const { id } = await setup({
moduleVariables: {
continue: "true",
report_tasks: "false",
ai_prompt: "test prompt",
},
});

const sessionId = "some-random-session-id";
const workdir = "/home/coder/project";
const claudeJson = {
projects: {
[workdir]: {
lastSessionId: sessionId,
},
},
};

await execContainer(id, [
"bash",
"-c",
`echo '${JSON.stringify(claudeJson)}' > /home/coder/.claude.json`,
]);

await execModuleScript(id);

const startLog = await execContainer(id, [
"bash",
"-c",
"cat /home/coder/.claude-module/agentapi-start.log",
]);
expect(startLog.stdout).toContain("--continue");
expect(startLog.stdout).toContain("Resuming existing session");
});

test("pre-post-install-scripts", async () => {
Expand Down
1 change: 1 addition & 0 deletions registry/coder/modules/claude-code/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ module "agentapi" {
ARG_PERMISSION_MODE='${var.permission_mode}' \
ARG_WORKDIR='${local.workdir}' \
ARG_AI_PROMPT='${base64encode(var.ai_prompt)}' \
ARG_REPORT_TASKS='${var.report_tasks}' \
ARG_ENABLE_BOUNDARY='${var.enable_boundary}' \
ARG_BOUNDARY_VERSION='${var.boundary_version}' \
ARG_BOUNDARY_LOG_DIR='${var.boundary_log_dir}' \
Expand Down
6 changes: 3 additions & 3 deletions registry/coder/modules/claude-code/main.tftest.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ run "test_claude_code_with_custom_options" {
group = "development"
icon = "/icon/custom.svg"
model = "opus"
task_prompt = "Help me write better code"
ai_prompt = "Help me write better code"
permission_mode = "plan"
continue = true
install_claude_code = false
Expand Down Expand Up @@ -88,8 +88,8 @@ run "test_claude_code_with_custom_options" {
}

assert {
condition = var.task_prompt == "Help me write better code"
error_message = "Task prompt variable should be set correctly"
condition = var.ai_prompt == "Help me write better code"
error_message = "AI prompt variable should be set correctly"
}

assert {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,19 @@ echo ".claude.json path $claude_json_path"
# Check if .claude.json exists
if [ ! -f "$claude_json_path" ]; then
echo "No .claude.json file found"
exit 0
exit 1
fi

# Use jq to check if lastSessionId exists for the working directory and remove it

if jq -e ".projects[\"$working_dir\"].lastSessionId" "$claude_json_path" > /dev/null 2>&1; then
# Remove lastSessionId and update the file
jq "del(.projects[\"$working_dir\"].lastSessionId)" "$claude_json_path" > "${claude_json_path}.tmp" && mv "${claude_json_path}.tmp" "$claude_json_path"
echo "Removed lastSessionId from .claude.json"
if jq "del(.projects[\"$working_dir\"].lastSessionId)" "$claude_json_path" > "${claude_json_path}.tmp" && mv "${claude_json_path}.tmp" "$claude_json_path"; then
echo "Removed lastSessionId from .claude.json"
exit 0
else
echo "Failed to remove lastSessionId from .claude.json"
fi
else
echo "No lastSessionId found in .claude.json - nothing to do"
fi
71 changes: 55 additions & 16 deletions registry/coder/modules/claude-code/scripts/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ ARG_DANGEROUSLY_SKIP_PERMISSIONS=${ARG_DANGEROUSLY_SKIP_PERMISSIONS:-}
ARG_PERMISSION_MODE=${ARG_PERMISSION_MODE:-}
ARG_WORKDIR=${ARG_WORKDIR:-"$HOME"}
ARG_AI_PROMPT=$(echo -n "${ARG_AI_PROMPT:-}" | base64 -d)
ARG_REPORT_TASKS=${ARG_REPORT_TASKS:-true}
ARG_ENABLE_BOUNDARY=${ARG_ENABLE_BOUNDARY:-false}
ARG_BOUNDARY_VERSION=${ARG_BOUNDARY_VERSION:-"main"}
ARG_BOUNDARY_LOG_DIR=${ARG_BOUNDARY_LOG_DIR:-"/tmp/boundary_logs"}
Expand All @@ -38,6 +39,7 @@ printf "ARG_DANGEROUSLY_SKIP_PERMISSIONS: %s\n" "$ARG_DANGEROUSLY_SKIP_PERMISSIO
printf "ARG_PERMISSION_MODE: %s\n" "$ARG_PERMISSION_MODE"
printf "ARG_AI_PROMPT: %s\n" "$ARG_AI_PROMPT"
printf "ARG_WORKDIR: %s\n" "$ARG_WORKDIR"
printf "ARG_REPORT_TASKS: %s\n" "$ARG_REPORT_TASKS"
printf "ARG_ENABLE_BOUNDARY: %s\n" "$ARG_ENABLE_BOUNDARY"
printf "ARG_BOUNDARY_VERSION: %s\n" "$ARG_BOUNDARY_VERSION"
printf "ARG_BOUNDARY_LOG_DIR: %s\n" "$ARG_BOUNDARY_LOG_DIR"
Expand All @@ -47,10 +49,18 @@ printf "ARG_CODER_HOST: %s\n" "$ARG_CODER_HOST"

echo "--------------------------------"

# see the remove-last-session-id.sh script for details
# about why we need it
# avoid exiting if the script fails
bash "/tmp/remove-last-session-id.sh" "$(pwd)" 2> /dev/null || true
# Clean up stale session data (see remove-last-session-id.sh for details)
CAN_CONTINUE_CONVERSATION=false
set +e
bash "/tmp/remove-last-session-id.sh" "$(pwd)" 2> /dev/null
session_cleanup_exit_code=$?
set -e

case $session_cleanup_exit_code in
0)
CAN_CONTINUE_CONVERSATION=true
;;
esac

function install_boundary() {
# Install boundary from public github repo
Expand All @@ -69,10 +79,15 @@ function validate_claude_installation() {
fi
}

# Hardcoded task session ID for Coder task reporting
# This ensures all task sessions use a consistent, predictable ID
TASK_SESSION_ID="cd32e253-ca16-4fd3-9825-d837e74ae3c2"

task_session_exists() {
if find "$HOME/.claude" -type f -name "*${TASK_SESSION_ID}*" 2> /dev/null | grep -q .; then
local workdir_normalized=$(echo "$ARG_WORKDIR" | tr '/' '-')
local project_dir="$HOME/.claude/projects/${workdir_normalized}"

if [ -d "$project_dir" ] && find "$project_dir" -type f -name "*${TASK_SESSION_ID}*" 2> /dev/null | grep -q .; then
return 0
else
return 1
Expand All @@ -97,39 +112,63 @@ function start_agentapi() {
fi

if [ -n "$ARG_RESUME_SESSION_ID" ]; then
echo "Using explicit resume_session_id: $ARG_RESUME_SESSION_ID"
echo "Resuming task session by ID: $ARG_RESUME_SESSION_ID"
ARGS+=(--resume "$ARG_RESUME_SESSION_ID")
if [ "$ARG_DANGEROUSLY_SKIP_PERMISSIONS" = "true" ]; then
ARGS+=(--dangerously-skip-permissions)
fi
elif [ "$ARG_CONTINUE" = "true" ]; then
if task_session_exists; then
if [ "$ARG_REPORT_TASKS" = "true" ] && task_session_exists; then
echo "Task session detected (ID: $TASK_SESSION_ID)"
ARGS+=(--resume "$TASK_SESSION_ID")
ARGS+=(--dangerously-skip-permissions)
echo "Resuming existing task session"
elif [ "$ARG_REPORT_TASKS" = "false" ] && [ "$CAN_CONTINUE_CONVERSATION" = true ]; then
echo "Previous session exists"
ARGS+=(--continue)
if [ "$ARG_DANGEROUSLY_SKIP_PERMISSIONS" = "true" ]; then
ARGS+=(--dangerously-skip-permissions)
fi
echo "Resuming existing task session"
echo "Resuming existing session"
else
echo "No existing task session found"
ARGS+=(--session-id "$TASK_SESSION_ID")
echo "No existing session found"
if [ "$ARG_REPORT_TASKS" = "true" ]; then
ARGS+=(--session-id "$TASK_SESSION_ID")
fi
if [ -n "$ARG_AI_PROMPT" ]; then
ARGS+=(--dangerously-skip-permissions "$ARG_AI_PROMPT")
echo "Starting new task session with prompt"
if [ "$ARG_REPORT_TASKS" = "true" ]; then
ARGS+=(--dangerously-skip-permissions "$ARG_AI_PROMPT")
else
if [ "$ARG_DANGEROUSLY_SKIP_PERMISSIONS" = "true" ]; then
ARGS+=(--dangerously-skip-permissions)
fi
ARGS+=("$ARG_AI_PROMPT")
fi
echo "Starting new session with prompt"
else
if [ "$ARG_DANGEROUSLY_SKIP_PERMISSIONS" = "true" ]; then
if [ "$ARG_REPORT_TASKS" = "true" ] || [ "$ARG_DANGEROUSLY_SKIP_PERMISSIONS" = "true" ]; then
ARGS+=(--dangerously-skip-permissions)
fi
echo "Starting new task session"
echo "Starting new session"
fi
fi
else
echo "Continue disabled, starting fresh session"
if [ "$ARG_REPORT_TASKS" = "true" ]; then
ARGS+=(--session-id "$TASK_SESSION_ID")
fi
if [ -n "$ARG_AI_PROMPT" ]; then
ARGS+=(--dangerously-skip-permissions "$ARG_AI_PROMPT")
if [ "$ARG_REPORT_TASKS" = "true" ]; then
ARGS+=(--dangerously-skip-permissions "$ARG_AI_PROMPT")
else
if [ "$ARG_DANGEROUSLY_SKIP_PERMISSIONS" = "true" ]; then
ARGS+=(--dangerously-skip-permissions)
fi
ARGS+=("$ARG_AI_PROMPT")
fi
echo "Starting new session with prompt"
else
if [ "$ARG_DANGEROUSLY_SKIP_PERMISSIONS" = "true" ]; then
if [ "$ARG_REPORT_TASKS" = "true" ] || [ "$ARG_DANGEROUSLY_SKIP_PERMISSIONS" = "true" ]; then
ARGS+=(--dangerously-skip-permissions)
fi
echo "Starting claude code session"
Expand Down