diff --git a/registry/coder/modules/claude-code/README.md b/registry/coder/modules/claude-code/README.md index 11e3caaec..6376c3716 100644 --- a/registry/coder/modules/claude-code/README.md +++ b/registry/coder/modules/claude-code/README.md @@ -13,8 +13,8 @@ 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.2.4" - agent_id = coder_agent.example.id + version = "4.2.5" + agent_id = coder_agent.main.id workdir = "/home/coder/project" claude_api_key = "xxxx-xxxxx-xxxx" } @@ -45,13 +45,15 @@ This example shows how to configure the Claude Code module to run the agent behi ```tf module "claude-code" { source = "dev.registry.coder.com/coder/claude-code/coder" + version = "4.2.5" + agent_id = coder_agent.main.id + workdir = "/home/coder/project" enable_boundary = true boundary_version = "main" boundary_log_dir = "/tmp/boundary_logs" boundary_log_level = "WARN" boundary_additional_allowed_urls = ["GET *google.com"] boundary_proxy_port = "8087" - version = "4.2.4" } ``` @@ -70,8 +72,8 @@ data "coder_parameter" "ai_prompt" { module "claude-code" { source = "registry.coder.com/coder/claude-code/coder" - version = "4.2.4" - agent_id = coder_agent.example.id + version = "4.2.5" + agent_id = coder_agent.main.id workdir = "/home/coder/project" claude_api_key = "xxxx-xxxxx-xxxx" @@ -106,13 +108,12 @@ 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 = "4.2.4" - agent_id = coder_agent.example.id - workdir = "/home/coder" + version = "4.2.5" + agent_id = coder_agent.main.id + workdir = "/home/coder/project" install_claude_code = true claude_code_version = "2.0.62" report_tasks = false - cli_app = true } ``` @@ -129,8 +130,8 @@ variable "claude_code_oauth_token" { module "claude-code" { source = "registry.coder.com/coder/claude-code/coder" - version = "4.2.4" - agent_id = coder_agent.example.id + version = "4.2.5" + agent_id = coder_agent.main.id workdir = "/home/coder/project" claude_code_oauth_token = var.claude_code_oauth_token } @@ -146,13 +147,13 @@ Configure Claude Code to use AWS Bedrock for accessing Claude models through you ```tf resource "coder_env" "bedrock_use" { - agent_id = coder_agent.example.id + agent_id = coder_agent.main.id name = "CLAUDE_CODE_USE_BEDROCK" value = "1" } resource "coder_env" "aws_region" { - agent_id = coder_agent.example.id + agent_id = coder_agent.main.id name = "AWS_REGION" value = "us-east-1" # Choose your preferred region } @@ -174,13 +175,13 @@ variable "aws_secret_access_key" { } resource "coder_env" "aws_access_key_id" { - agent_id = coder_agent.example.id + agent_id = coder_agent.main.id name = "AWS_ACCESS_KEY_ID" value = var.aws_access_key_id } resource "coder_env" "aws_secret_access_key" { - agent_id = coder_agent.example.id + agent_id = coder_agent.main.id name = "AWS_SECRET_ACCESS_KEY" value = var.aws_secret_access_key } @@ -195,15 +196,15 @@ variable "aws_bearer_token_bedrock" { } resource "coder_env" "bedrock_api_key" { - agent_id = coder_agent.example.id + agent_id = coder_agent.main.id name = "AWS_BEARER_TOKEN_BEDROCK" value = var.aws_bearer_token_bedrock } module "claude-code" { source = "registry.coder.com/coder/claude-code/coder" - version = "4.2.4" - agent_id = coder_agent.example.id + version = "4.2.5" + agent_id = coder_agent.main.id workdir = "/home/coder/project" model = "global.anthropic.claude-sonnet-4-5-20250929-v1:0" } @@ -228,39 +229,39 @@ variable "vertex_sa_json" { } resource "coder_env" "vertex_use" { - agent_id = coder_agent.example.id + agent_id = coder_agent.main.id name = "CLAUDE_CODE_USE_VERTEX" value = "1" } resource "coder_env" "vertex_project_id" { - agent_id = coder_agent.example.id + agent_id = coder_agent.main.id name = "ANTHROPIC_VERTEX_PROJECT_ID" value = "your-gcp-project-id" } resource "coder_env" "cloud_ml_region" { - agent_id = coder_agent.example.id + agent_id = coder_agent.main.id name = "CLOUD_ML_REGION" value = "global" } resource "coder_env" "vertex_sa_json" { - agent_id = coder_agent.example.id + agent_id = coder_agent.main.id name = "VERTEX_SA_JSON" value = var.vertex_sa_json } resource "coder_env" "google_application_credentials" { - agent_id = coder_agent.example.id + agent_id = coder_agent.main.id name = "GOOGLE_APPLICATION_CREDENTIALS" value = "/tmp/gcp-sa.json" } module "claude-code" { source = "registry.coder.com/coder/claude-code/coder" - version = "4.2.4" - agent_id = coder_agent.example.id + version = "4.2.5" + agent_id = coder_agent.main.id workdir = "/home/coder/project" model = "claude-sonnet-4@20250514" diff --git a/registry/coder/modules/claude-code/main.test.ts b/registry/coder/modules/claude-code/main.test.ts index 94fcb391a..6e37501da 100644 --- a/registry/coder/modules/claude-code/main.test.ts +++ b/registry/coder/modules/claude-code/main.test.ts @@ -208,13 +208,17 @@ describe("claude-code", async () => { }); // Create a mock task session file with the hardcoded task session ID + // Note: Claude CLI creates files without "session-" prefix when using --session-id const taskSessionId = "cd32e253-ca16-4fd3-9825-d837e74ae3c2"; const sessionDir = `/home/coder/.claude/projects/-home-coder-project`; await execContainer(id, ["mkdir", "-p", sessionDir]); await execContainer(id, [ "bash", "-c", - `touch ${sessionDir}/session-${taskSessionId}.jsonl`, + `cat > ${sessionDir}/${taskSessionId}.jsonl << 'SESSIONEOF' +{"sessionId":"${taskSessionId}","message":{"content":"Task"},"timestamp":"2020-01-01T10:00:00.000Z"} +{"type":"assistant","message":{"content":"Response"},"timestamp":"2020-01-01T10:00:05.000Z"} +SESSIONEOF`, ]); await execModuleScript(id); @@ -226,46 +230,10 @@ 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("Resuming 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 () => { const { id } = await setup({ moduleVariables: { @@ -360,4 +328,140 @@ describe("claude-code", async () => { "ARG_AGENTAPI_CHAT_BASE_PATH=/@default/default.foo/apps/ccw/chat", ); }); + + test("partial-initialization-detection", async () => { + const { id } = await setup({ + moduleVariables: { + continue: "true", + report_tasks: "true", + ai_prompt: "test prompt", + }, + }); + + const taskSessionId = "cd32e253-ca16-4fd3-9825-d837e74ae3c2"; + const sessionDir = `/home/coder/.claude/projects/-home-coder-project`; + await execContainer(id, ["mkdir", "-p", sessionDir]); + + await execContainer(id, [ + "bash", + "-c", + `echo '{"sessionId":"${taskSessionId}"}' > ${sessionDir}/${taskSessionId}.jsonl`, + ]); + + await execModuleScript(id); + + const startLog = await execContainer(id, [ + "bash", + "-c", + "cat /home/coder/.claude-module/agentapi-start.log", + ]); + + // Should start new session, not try to resume invalid one + expect(startLog.stdout).toContain("Starting new task session"); + expect(startLog.stdout).toContain("--session-id"); + }); + + test("standalone-first-build-no-sessions", async () => { + const { id } = await setup({ + moduleVariables: { + continue: "true", + report_tasks: "false", + }, + }); + + await execModuleScript(id); + + const startLog = await execContainer(id, [ + "bash", + "-c", + "cat /home/coder/.claude-module/agentapi-start.log", + ]); + + // Should start fresh, not try to continue + expect(startLog.stdout).toContain("No sessions found"); + expect(startLog.stdout).toContain("starting fresh standalone session"); + expect(startLog.stdout).not.toContain("--continue"); + }); + + test("standalone-with-sessions-continues", async () => { + const { id } = await setup({ + moduleVariables: { + continue: "true", + report_tasks: "false", + }, + }); + + const sessionDir = `/home/coder/.claude/projects/-home-coder-project`; + await execContainer(id, ["mkdir", "-p", sessionDir]); + await execContainer(id, [ + "bash", + "-c", + `cat > ${sessionDir}/generic-123.jsonl << 'EOF' +{"sessionId":"generic-123","message":{"content":"User session"},"timestamp":"2020-01-01T10:00:00.000Z"} +{"type":"assistant","message":{"content":"Response"},"timestamp":"2020-01-01T10:00:05.000Z"} +EOF`, + ]); + + await execModuleScript(id); + + const startLog = await execContainer(id, [ + "bash", + "-c", + "cat /home/coder/.claude-module/agentapi-start.log", + ]); + + // Should continue existing session + expect(startLog.stdout).toContain("Sessions found"); + expect(startLog.stdout).toContain( + "Continuing most recent standalone session", + ); + expect(startLog.stdout).toContain("--continue"); + }); + + test("task-mode-ignores-manual-sessions", async () => { + const { id } = await setup({ + moduleVariables: { + continue: "true", + report_tasks: "true", + ai_prompt: "test prompt", + }, + }); + + const taskSessionId = "cd32e253-ca16-4fd3-9825-d837e74ae3c2"; + const sessionDir = `/home/coder/.claude/projects/-home-coder-project`; + await execContainer(id, ["mkdir", "-p", sessionDir]); + + // Create task session (without "session-" prefix, as CLI does) + await execContainer(id, [ + "bash", + "-c", + `cat > ${sessionDir}/${taskSessionId}.jsonl << 'EOF' +{"sessionId":"${taskSessionId}","message":{"content":"Task"},"timestamp":"2020-01-01T10:00:00.000Z"} +{"type":"assistant","message":{"content":"Response"},"timestamp":"2020-01-01T10:00:05.000Z"} +EOF`, + ]); + + // Create manual session (newer) + await execContainer(id, [ + "bash", + "-c", + `cat > ${sessionDir}/manual-456.jsonl << 'EOF' +{"sessionId":"manual-456","message":{"content":"Manual"},"timestamp":"2020-01-02T10:00:00.000Z"} +{"type":"assistant","message":{"content":"Response"},"timestamp":"2020-01-02T10:00:05.000Z"} +EOF`, + ]); + + await execModuleScript(id); + + const startLog = await execContainer(id, [ + "bash", + "-c", + "cat /home/coder/.claude-module/agentapi-start.log", + ]); + + // Should resume task session, not manual session + expect(startLog.stdout).toContain("Resuming task session"); + expect(startLog.stdout).toContain(taskSessionId); + expect(startLog.stdout).not.toContain("manual-456"); + }); }); diff --git a/registry/coder/modules/claude-code/main.tf b/registry/coder/modules/claude-code/main.tf index 24c0cc37e..099d2a1e1 100644 --- a/registry/coder/modules/claude-code/main.tf +++ b/registry/coder/modules/claude-code/main.tf @@ -291,12 +291,11 @@ resource "coder_env" "disable_autoupdater" { locals { # we have to trim the slash because otherwise coder exp mcp will # set up an invalid claude config - workdir = trimsuffix(var.workdir, "/") - app_slug = "ccw" - install_script = file("${path.module}/scripts/install.sh") - start_script = file("${path.module}/scripts/start.sh") - module_dir_name = ".claude-module" - remove_last_session_id_script_b64 = base64encode(file("${path.module}/scripts/remove-last-session-id.sh")) + workdir = trimsuffix(var.workdir, "/") + app_slug = "ccw" + install_script = file("${path.module}/scripts/install.sh") + start_script = file("${path.module}/scripts/start.sh") + module_dir_name = ".claude-module" # Extract hostname from access_url for boundary --allow flag coder_host = replace(replace(data.coder_workspace.me.access_url, "https://", ""), "http://", "") @@ -357,9 +356,7 @@ module "agentapi" { set -o errexit set -o pipefail echo -n '${base64encode(local.start_script)}' | base64 -d > /tmp/start.sh - echo -n "${local.remove_last_session_id_script_b64}" | base64 -d > "/tmp/remove-last-session-id.sh" chmod +x /tmp/start.sh - chmod +x /tmp/remove-last-session-id.sh ARG_MODEL='${var.model}' \ ARG_RESUME_SESSION_ID='${var.resume_session_id}' \ diff --git a/registry/coder/modules/claude-code/scripts/install.sh b/registry/coder/modules/claude-code/scripts/install.sh index 8aa0d1ac8..ba4420fa3 100644 --- a/registry/coder/modules/claude-code/scripts/install.sh +++ b/registry/coder/modules/claude-code/scripts/install.sh @@ -90,12 +90,63 @@ function setup_claude_configurations() { } +function configure_standalone_mode() { + echo "Configuring Claude Code for standalone mode..." + + if [ -z "${CLAUDE_API_KEY:-}" ]; then + echo "Note: CLAUDE_API_KEY not set, skipping authentication setup" + return + fi + + local claude_config="$HOME/.claude.json" + local workdir_normalized + workdir_normalized=$(echo "$ARG_WORKDIR" | tr '/' '-') + + # Create or update .claude.json with minimal configuration for API key auth + # This skips the interactive login prompt and onboarding screens + if [ -f "$claude_config" ]; then + echo "Updating existing Claude configuration at $claude_config" + + jq --arg apikey "${CLAUDE_API_KEY:-}" \ + --arg workdir "$ARG_WORKDIR" \ + '.autoUpdaterStatus = "disabled" | + .bypassPermissionsModeAccepted = true | + .hasAcknowledgedCostThreshold = true | + .hasCompletedOnboarding = true | + .primaryApiKey = $apikey | + .projects[$workdir].hasCompletedProjectOnboarding = true | + .projects[$workdir].hasTrustDialogAccepted = true' \ + "$claude_config" > "${claude_config}.tmp" && mv "${claude_config}.tmp" "$claude_config" + else + echo "Creating new Claude configuration at $claude_config" + cat > "$claude_config" << EOF +{ + "autoUpdaterStatus": "disabled", + "bypassPermissionsModeAccepted": true, + "hasAcknowledgedCostThreshold": true, + "hasCompletedOnboarding": true, + "primaryApiKey": "${CLAUDE_API_KEY:-}", + "projects": { + "$ARG_WORKDIR": { + "hasCompletedProjectOnboarding": true, + "hasTrustDialogAccepted": true + } + } +} +EOF + fi + + echo "Standalone mode configured successfully" +} + function report_tasks() { if [ "$ARG_REPORT_TASKS" = "true" ]; then echo "Configuring Claude Code to report tasks via Coder MCP..." export CODER_MCP_APP_STATUS_SLUG="$ARG_MCP_APP_STATUS_SLUG" export CODER_MCP_AI_AGENTAPI_URL="http://localhost:3284" coder exp mcp configure claude-code "$ARG_WORKDIR" + else + configure_standalone_mode fi } diff --git a/registry/coder/modules/claude-code/scripts/remove-last-session-id.sh b/registry/coder/modules/claude-code/scripts/remove-last-session-id.sh deleted file mode 100755 index d72369fa7..000000000 --- a/registry/coder/modules/claude-code/scripts/remove-last-session-id.sh +++ /dev/null @@ -1,44 +0,0 @@ -# If lastSessionId is present in .claude.json, claude --continue will start a -# conversation starting from that session. The problem is that lastSessionId -# doesn't always point to the last session. The field is updated by claude only -# at the point of normal CLI exit. If Claude exits with an error, or if the user -# restarts the Coder workspace, lastSessionId will be stale, and claude --continue -# will start from an old session. -# -# If lastSessionId is missing, claude seems to accurately figure out where to -# start using the conversation history - even if the CLI previously exited with -# an error. -# -# This script removes the lastSessionId field from .claude.json. -if [ $# -eq 0 ]; then - echo "No working directory provided - it must be the first argument" - exit 1 -fi - -# Get absolute path of working directory -working_dir=$(realpath "$1") -echo "workingDir $working_dir" - -# Path to .claude.json -claude_json_path="$HOME/.claude.json" -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 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 - 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 diff --git a/registry/coder/modules/claude-code/scripts/start.sh b/registry/coder/modules/claude-code/scripts/start.sh index 59983b9e8..93ff4f723 100644 --- a/registry/coder/modules/claude-code/scripts/start.sh +++ b/registry/coder/modules/claude-code/scripts/start.sh @@ -51,19 +51,6 @@ printf "ARG_CODER_HOST: %s\n" "$ARG_CODER_HOST" echo "--------------------------------" -# 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() { if [ "${ARG_COMPILE_FROM_SOURCE:-false}" = "true" ]; then # Install boundary by compiling from source @@ -99,18 +86,85 @@ function validate_claude_installation() { # This ensures all task sessions use a consistent, predictable ID TASK_SESSION_ID="cd32e253-ca16-4fd3-9825-d837e74ae3c2" -task_session_exists() { +get_project_dir() { local workdir_normalized workdir_normalized=$(echo "$ARG_WORKDIR" | tr '/' '-') - local project_dir="$HOME/.claude/projects/${workdir_normalized}" + echo "$HOME/.claude/projects/${workdir_normalized}" +} - printf "PROJECT_DIR: %s, workdir_normalized: %s\n" "$project_dir" "$workdir_normalized" +get_task_session_file() { + echo "$(get_project_dir)/${TASK_SESSION_ID}.jsonl" +} - if [ -d "$project_dir" ] && find "$project_dir" -type f -name "*${TASK_SESSION_ID}*" 2> /dev/null | grep -q .; then - printf "TASK_SESSION_ID: %s file found\n" "$TASK_SESSION_ID" +task_session_exists() { + local session_file + session_file=$(get_task_session_file) + + if [ -f "$session_file" ]; then + printf "Task session file found: %s\n" "$session_file" return 0 else - printf "TASK_SESSION_ID: %s file not found\n" "$TASK_SESSION_ID" + printf "Task session file not found: %s\n" "$session_file" + return 1 + fi +} + +is_valid_session() { + local session_file="$1" + + # Check if file exists and is not empty + # Empty files indicate the session was created but never used so they need to be removed + if [ ! -f "$session_file" ]; then + printf "Session validation failed: file does not exist\n" + return 1 + fi + + if [ ! -s "$session_file" ]; then + printf "Session validation failed: file is empty, removing stale file\n" + rm -f "$session_file" + return 1 + fi + + # Check for minimum session content + # Valid sessions need at least 2 lines: initial message and first response + local line_count + line_count=$(wc -l < "$session_file") + if [ "$line_count" -lt 2 ]; then + printf "Session validation failed: incomplete (only %s lines), removing incomplete file\n" "$line_count" + rm -f "$session_file" + return 1 + fi + + # Validate JSONL format by checking first 3 lines + # Claude session files use JSONL (JSON Lines) format where each line is valid JSON + if ! head -3 "$session_file" | jq empty 2> /dev/null; then + printf "Session validation failed: invalid JSONL format, removing corrupt file\n" + rm -f "$session_file" + return 1 + fi + + # Verify the session has a valid sessionId field + # This ensures the file structure matches Claude's session format + if ! grep -q '"sessionId"' "$session_file" \ + || ! grep -m 1 '"sessionId"' "$session_file" | jq -e '.sessionId' > /dev/null 2>&1; then + printf "Session validation failed: no valid sessionId found, removing malformed file\n" + rm -f "$session_file" + return 1 + fi + + printf "Session validation passed: %s\n" "$session_file" + return 0 +} + +has_any_sessions() { + local project_dir + project_dir=$(get_project_dir) + + if [ -d "$project_dir" ] && find "$project_dir" -maxdepth 1 -name "*.jsonl" -size +0c 2> /dev/null | grep -q .; then + printf "Sessions found in: %s\n" "$project_dir" + return 0 + else + printf "No sessions found in: %s\n" "$project_dir" return 1 fi } @@ -133,75 +187,41 @@ function start_agentapi() { fi if [ -n "$ARG_RESUME_SESSION_ID" ]; then - echo "Resuming task session by ID: $ARG_RESUME_SESSION_ID" + echo "Resuming specified session: $ARG_RESUME_SESSION_ID" ARGS+=(--resume "$ARG_RESUME_SESSION_ID") - if [ "$ARG_DANGEROUSLY_SKIP_PERMISSIONS" = "true" ]; then - ARGS+=(--dangerously-skip-permissions) - fi + [ "$ARG_DANGEROUSLY_SKIP_PERMISSIONS" = "true" ] && ARGS+=(--dangerously-skip-permissions) + elif [ "$ARG_CONTINUE" = "true" ]; 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) + + if [ "$ARG_REPORT_TASKS" = "true" ]; then + local session_file + session_file=$(get_task_session_file) + + if task_session_exists && is_valid_session "$session_file"; then + echo "Resuming task session: $TASK_SESSION_ID" + ARGS+=(--resume "$TASK_SESSION_ID" --dangerously-skip-permissions) + else + echo "Starting new task session: $TASK_SESSION_ID" + ARGS+=(--session-id "$TASK_SESSION_ID" --dangerously-skip-permissions) + [ -n "$ARG_AI_PROMPT" ] && ARGS+=(-- "$ARG_AI_PROMPT") fi - echo "Resuming existing session" + else - echo "No existing session found" - if [ "$ARG_REPORT_TASKS" = "true" ]; then - if task_session_exists; then - ARGS+=(--resume "$TASK_SESSION_ID") - else - ARGS+=(--session-id "$TASK_SESSION_ID") - fi - fi - if [ -n "$ARG_AI_PROMPT" ]; then - 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" + if has_any_sessions; then + echo "Continuing most recent standalone session" + ARGS+=(--continue) + [ "$ARG_DANGEROUSLY_SKIP_PERMISSIONS" = "true" ] && ARGS+=(--dangerously-skip-permissions) else - if [ "$ARG_REPORT_TASKS" = "true" ] || [ "$ARG_DANGEROUSLY_SKIP_PERMISSIONS" = "true" ]; then - ARGS+=(--dangerously-skip-permissions) - fi - echo "Starting new session" + echo "No sessions found, starting fresh standalone session" + [ "$ARG_DANGEROUSLY_SKIP_PERMISSIONS" = "true" ] && ARGS+=(--dangerously-skip-permissions) + [ -n "$ARG_AI_PROMPT" ] && ARGS+=(-- "$ARG_AI_PROMPT") fi fi + else echo "Continue disabled, starting fresh session" - if [ "$ARG_REPORT_TASKS" = "true" ]; then - if task_session_exists; then - ARGS+=(--resume "$TASK_SESSION_ID") - else - ARGS+=(--session-id "$TASK_SESSION_ID") - fi - fi - if [ -n "$ARG_AI_PROMPT" ]; then - 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_REPORT_TASKS" = "true" ] || [ "$ARG_DANGEROUSLY_SKIP_PERMISSIONS" = "true" ]; then - ARGS+=(--dangerously-skip-permissions) - fi - echo "Starting claude code session" - fi + [ "$ARG_DANGEROUSLY_SKIP_PERMISSIONS" = "true" ] && ARGS+=(--dangerously-skip-permissions) + [ -n "$ARG_AI_PROMPT" ] && ARGS+=(-- "$ARG_AI_PROMPT") fi printf "Running claude code with args: %s\n" "$(printf '%q ' "${ARGS[@]}")"