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
2 changes: 1 addition & 1 deletion 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 = "4.9.1"
version = "4.9.2"
agent_id = coder_agent.main.id
workdir = "/home/coder/project"
claude_api_key = "xxxx-xxxxx-xxxx"
Expand Down
18 changes: 18 additions & 0 deletions registry/coder/modules/claude-code/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,24 @@ describe("claude-code", async () => {
expect(startLog.stdout).toContain(`--permission-mode ${mode}`);
});

test("claude-auto-permission-mode", async () => {
const mode = "auto";
const { id } = await setup({
moduleVariables: {
permission_mode: mode,
ai_prompt: "test prompt",
},
});
await execModuleScript(id);

const startLog = await execContainer(id, [
"bash",
"-c",
"cat /home/coder/.claude-module/agentapi-start.log",
]);
expect(startLog.stdout).toContain(`--permission-mode ${mode}`);
});

test("claude-model", async () => {
const model = "opus";
const { coderEnvVars } = await setup({
Expand Down
5 changes: 3 additions & 2 deletions registry/coder/modules/claude-code/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,8 @@ variable "permission_mode" {
description = "Permission mode for the cli, check https://docs.anthropic.com/en/docs/claude-code/iam#permission-modes"
default = ""
validation {
condition = contains(["", "default", "acceptEdits", "plan", "bypassPermissions"], var.permission_mode)
error_message = "interaction_mode must be one of: default, acceptEdits, plan, bypassPermissions."
condition = contains(["", "default", "acceptEdits", "plan", "auto", "bypassPermissions"], var.permission_mode)
error_message = "interaction_mode must be one of: default, acceptEdits, plan, auto, bypassPermissions."
}
}

Expand Down Expand Up @@ -430,6 +430,7 @@ module "agentapi" {
ARG_MCP='${var.mcp != null ? base64encode(replace(var.mcp, "'", "'\\''")) : ""}' \
ARG_MCP_CONFIG_REMOTE_PATH='${base64encode(jsonencode(var.mcp_config_remote_path))}' \
ARG_ENABLE_AIBRIDGE='${var.enable_aibridge}' \
ARG_PERMISSION_MODE='${var.permission_mode}' \
/tmp/install.sh
EOT
}
Expand Down
17 changes: 16 additions & 1 deletion registry/coder/modules/claude-code/main.tftest.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -183,11 +183,26 @@ run "test_claude_code_permission_mode_validation" {
}

assert {
condition = contains(["", "default", "acceptEdits", "plan", "bypassPermissions"], var.permission_mode)
condition = contains(["", "default", "acceptEdits", "plan", "auto", "bypassPermissions"], var.permission_mode)
error_message = "Permission mode should be one of the valid options"
}
}

run "test_claude_code_auto_permission_mode" {
command = plan

variables {
agent_id = "test-agent-auto"
workdir = "/home/coder/test"
permission_mode = "auto"
}

assert {
condition = var.permission_mode == "auto"
error_message = "Permission mode should be set to auto"
}
}

run "test_claude_code_with_boundary" {
command = plan

Expand Down
25 changes: 25 additions & 0 deletions registry/coder/modules/claude-code/scripts/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ ARG_MCP_CONFIG_REMOTE_PATH=$(echo -n "${ARG_MCP_CONFIG_REMOTE_PATH:-}" | base64
ARG_ALLOWED_TOOLS=${ARG_ALLOWED_TOOLS:-}
ARG_DISALLOWED_TOOLS=${ARG_DISALLOWED_TOOLS:-}
ARG_ENABLE_AIBRIDGE=${ARG_ENABLE_AIBRIDGE:-false}
ARG_PERMISSION_MODE=${ARG_PERMISSION_MODE:-}

export PATH="$ARG_CLAUDE_BINARY_PATH:$PATH"

Expand Down Expand Up @@ -195,6 +196,7 @@ function configure_standalone_mode() {

jq --arg workdir "$ARG_WORKDIR" --arg apikey "${CLAUDE_API_KEY:-}" \
'.autoUpdaterStatus = "disabled" |
.autoModeAccepted = true |
.bypassPermissionsModeAccepted = true |
.hasAcknowledgedCostThreshold = true |
.hasCompletedOnboarding = true |
Expand All @@ -207,6 +209,7 @@ function configure_standalone_mode() {
cat > "$claude_config" << EOF
{
"autoUpdaterStatus": "disabled",
"autoModeAccepted": true,
"bypassPermissionsModeAccepted": true,
"hasAcknowledgedCostThreshold": true,
"hasCompletedOnboarding": true,
Expand Down Expand Up @@ -235,6 +238,28 @@ function report_tasks() {
fi
}

function accept_auto_mode() {
# Pre-accept the auto mode TOS prompt so it doesn't appear interactively.
# Claude Code shows a confirmation dialog for auto mode that blocks
# non-interactive/headless usage.
# Note: bypassPermissions acceptance is already handled by
# coder exp mcp configure (task mode) and configure_standalone_mode.
local claude_config="$HOME/.claude.json"

if [ -f "$claude_config" ]; then
jq '.autoModeAccepted = true' \
"$claude_config" > "${claude_config}.tmp" && mv "${claude_config}.tmp" "$claude_config"
else
echo '{"autoModeAccepted": true}' > "$claude_config"
fi

echo "Pre-accepted auto mode prompt"
}

install_claude_code_cli
setup_claude_configurations
report_tasks

if [ "$ARG_PERMISSION_MODE" = "auto" ]; then
accept_auto_mode
fi