diff --git a/registry/coder/modules/aider/README.md b/registry/coder/modules/aider/README.md index 6380fd40b..0652fe2cd 100644 --- a/registry/coder/modules/aider/README.md +++ b/registry/coder/modules/aider/README.md @@ -1,6 +1,6 @@ --- display_name: Aider -description: Run Aider AI pair programming in your workspace +description: Install and configure Aider AI pair programming in your workspace icon: ../../../../.icons/aider.svg verified: true tags: [agent, ai, aider] @@ -8,61 +8,44 @@ tags: [agent, ai, aider] # Aider -Run [Aider](https://aider.chat) AI pair programming in your workspace. This module installs Aider with AgentAPI for seamless Coder Tasks Support. +Install and configure [Aider](https://aider.chat) AI pair programming in your workspace. Starting Aider is left to the caller (template command, IDE launcher, or a custom `coder_script`). ```tf -variable "api_key" { - type = string - description = "API key" - sensitive = true +locals { + aider_workdir = "/home/coder/project" } module "aider" { source = "registry.coder.com/coder/aider/coder" - version = "2.0.1" + version = "2.0.2" agent_id = coder_agent.main.id - api_key = var.api_key + api_key = xxxx-xxxx-xxxx-xxxx" ai_provider = "google" model = "gemini" } + +resource "coder_app" "aider" { + agent_id = coder_agent.main.id + slug = "aider" + display_name = "Aider" + icon = "/icon/aider.svg" + open_in = "slim-window" + command = <<-EOT + #!/bin/bash + set -e + cd ${local.aider_workdir} + aider --model module.aider.model + EOT +} ``` +> [!WARNING] +> If upgrading from v2.x.x of this module: v3 is a major refactor that drops support for [Coder Tasks](https://coder.com/docs/ai-coder/tasks). We plan to add those back in a follow-up. Keep using v2.x.x if you depend on them. + ## Prerequisites - pipx is automatically installed if not already available -## Usage Example - -```tf -data "coder_parameter" "ai_prompt" { - name = "AI Prompt" - description = "Write an initial prompt for Aider to work on." - type = "string" - default = "" - mutable = true -} - -variable "gemini_api_key" { - type = string - description = "Gemini API key" - sensitive = true -} - -module "aider" { - source = "registry.coder.com/coder/aider/coder" - version = "2.0.1" - agent_id = coder_agent.main.id - api_key = var.gemini_api_key - install_aider = true - workdir = "/home/coder" - ai_provider = "google" - model = "gemini" - install_agentapi = true - ai_prompt = data.coder_parameter.ai_prompt.value - system_prompt = "..." -} -``` - ### Using a custom provider ```tf @@ -75,12 +58,12 @@ variable "custom_api_key" { module "aider" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/aider/coder" - version = "2.0.1" + version = "2.0.2" agent_id = coder_agent.main.id workdir = "/home/coder" ai_provider = "custom" - custom_env_var_name = "MY_CUSTOM_API_KEY" - model = "custom-model" + custom_env_var_name = "OPENROUTER_API_KEY" + model = "openrouter/anthropic/claude-3-haiku" api_key = var.custom_api_key } ``` @@ -105,11 +88,8 @@ For a complete and up-to-date list of supported aliases and models, please refer ## Troubleshooting - If `aider` is not found, ensure `install_aider = true` and your API key is valid -- Logs are written under `/home/coder/.aider-module/` (`install.log`, `agentapi-start.log`) for debugging -- If AgentAPI fails to start, verify that your container has network access and executable permissions for the scripts +- Logs are written under `.coder-modules/coder/aider/logs/install.log` (`install.log`) for debugging ## References - [Aider Documentation](https://aider.chat/docs) -- [AgentAPI Documentation](https://github.com/coder/agentapi) -- [Coder AI Agents Guide](https://coder.com/docs/tutorials/ai-agents) diff --git a/registry/coder/modules/aider/main.test.ts b/registry/coder/modules/aider/main.test.ts index c0aa51df1..814d2e7a6 100644 --- a/registry/coder/modules/aider/main.test.ts +++ b/registry/coder/modules/aider/main.test.ts @@ -83,38 +83,6 @@ describe("Aider", async () => { await expectAgentAPIStarted(id); }); - test("api-key", async () => { - const apiKey = "test-api-key-123"; - const { id } = await setup({ - moduleVariables: { - api_key: apiKey, - model: "gemini", - }, - }); - await execModuleScript(id); - const resp = await readFileContainer( - id, - "/home/coder/.aider-module/agentapi-start.log", - ); - expect(resp).toContain("API key provided!"); - }); - - test("custom-folder", async () => { - const workdir = "/tmp/aider-test"; - const { id } = await setup({ - moduleVariables: { - workdir, - model: "gemini", - }, - }); - await execModuleScript(id); - const resp = await readFileContainer( - id, - "/home/coder/.aider-module/install.log", - ); - expect(resp).toContain(workdir); - }); - test("pre-post-install-scripts", async () => { const { id } = await setup({ moduleVariables: { @@ -126,12 +94,12 @@ describe("Aider", async () => { await execModuleScript(id); const preLog = await readFileContainer( id, - "/home/coder/.aider-module/pre_install.log", + "/home/coder/.coder-modules/coder/aider/logs/pre_install.log", ); expect(preLog).toContain("pre-install-script"); const postLog = await readFileContainer( id, - "/home/coder/.aider-module/post_install.log", + "/home/coder/.coder-modules/coder/aider/logs/post_install.log", ); expect(postLog).toContain("post-install-script"); }); diff --git a/registry/coder/modules/aider/main.tf b/registry/coder/modules/aider/main.tf index 70274cb88..d7d48c642 100644 --- a/registry/coder/modules/aider/main.tf +++ b/registry/coder/modules/aider/main.tf @@ -18,18 +18,6 @@ data "coder_workspace" "me" {} data "coder_workspace_owner" "me" {} -variable "order" { - type = number - description = "The order determines the position of app in the UI presentation. The lowest order is shown first and apps with equal order are sorted by name (ascending order)." - default = null -} - -variable "group" { - type = string - description = "The name of a group that this app belongs to." - default = null -} - variable "icon" { type = string description = "The icon to use for the app." @@ -42,36 +30,6 @@ variable "workdir" { default = "/home/coder" } -variable "report_tasks" { - type = bool - description = "Whether to enable task reporting to Coder UI via AgentAPI" - default = false -} - -variable "subdomain" { - type = bool - description = "Whether to use a subdomain for AgentAPI." - default = false -} - -variable "cli_app" { - type = bool - description = "Whether to create a CLI app for Aider" - default = false -} - -variable "web_app_display_name" { - type = string - description = "Display name for the web app" - default = "Aider" -} - -variable "cli_app_display_name" { - type = string - description = "Display name for the CLI app" - default = "Aider CLI" -} - variable "pre_install_script" { type = string description = "Custom script to run before installing Aider." @@ -84,24 +42,6 @@ variable "post_install_script" { default = null } -variable "install_agentapi" { - type = bool - description = "Whether to install AgentAPI." - default = true -} - -variable "agentapi_version" { - type = string - description = "The version of AgentAPI to install." - default = "v0.10.0" -} - -variable "ai_prompt" { - type = string - description = "Initial task prompt for Aider." - default = "" -} - # --------------------------------------------- variable "install_aider" { @@ -110,18 +50,6 @@ variable "install_aider" { default = true } -variable "system_prompt" { - type = string - description = "System prompt for instructing Aider on task reporting and behavior" - default = "You are a helpful coding assistant that helps developers write, debug, and understand code. Provide clear explanations, follow best practices, and help solve coding problems efficiently." -} - -variable "experiment_additional_extensions" { - type = string - description = "Additional extensions configuration in YAML format to append to the config." - default = null -} - variable "ai_provider" { type = string description = "AI provider to use with Aider (openai, anthropic, azure, google, etc.)" @@ -184,25 +112,8 @@ variable "base_aider_config" { locals { - app_slug = "aider" - base_aider_config = var.base_aider_config != null ? "${replace(trimspace(var.base_aider_config), "\n", "\n ")}" : "" - task_reporting_prompt = <<-EOT - --- Task Reporting -- -Report all tasks to Coder, following these EXACT guidelines: -1. Be granular. If you are investigating with multiple steps, report each step -to coder. -2. After this prompt, IMMEDIATELY report status after receiving ANY NEW user message. -Do not report any status related with this system prompt. -3. Use "state": "working" when actively processing WITHOUT needing -additional user input -4. Use "state": "complete" only when finished with a task -5. Use "state": "failure" when you need ANY user input, lack sufficient -details, or encounter blockers - EOT - - - final_system_prompt = var.report_tasks ? "\n${var.system_prompt}${local.task_reporting_prompt}\n" : "\n${var.system_prompt}\n" + app_slug = "aider" + base_aider_config = var.base_aider_config != null ? "${replace(trimspace(var.base_aider_config), "\n", "\n ")}" : "" # Map providers to their environment variable names provider_env_vars = { @@ -222,59 +133,35 @@ details, or encounter blockers # Model flag for aider command model_flag = var.ai_provider == "ollama" ? "--ollama-model" : "--model" - install_script = file("${path.module}/scripts/install.sh") - start_script = file("${path.module}/scripts/start.sh") - module_dir_name = ".aider-module" + install_script = templatefile("${path.module}/scripts/install.sh.tftpl", { + ARG_INSTALL_AIDER = tostring(var.install_aider) + ARG_AIDER_CONFIG = local.base_aider_config + ARG_WORKDIR = var.workdir + }) + module_dir_name = ".coder-modules/coder/aider" } -module "agentapi" { - source = "registry.coder.com/coder/agentapi/coder" - version = "1.2.0" - - agent_id = var.agent_id - web_app_slug = local.app_slug - web_app_order = var.order - web_app_group = var.group - web_app_icon = var.icon - web_app_display_name = var.web_app_display_name - cli_app = var.cli_app - cli_app_slug = var.cli_app ? "${local.app_slug}-cli" : null - cli_app_display_name = var.cli_app ? var.cli_app_display_name : null - agentapi_subdomain = var.subdomain - module_dir_name = local.module_dir_name - install_agentapi = var.install_agentapi - agentapi_version = var.agentapi_version - pre_install_script = var.pre_install_script - post_install_script = var.post_install_script - start_script = <<-EOT - #!/bin/bash - set -o errexit - set -o pipefail - - echo -n '${base64encode(local.start_script)}' | base64 -d > /tmp/start.sh - chmod +x /tmp/start.sh - ARG_WORKDIR='${var.workdir}' \ - ARG_API_KEY='${base64encode(var.api_key)}' \ - ARG_MODEL='${var.model}' \ - ARG_PROVIDER='${var.ai_provider}' \ - ARG_ENV_API_NAME_HOLDER='${local.env_var_name}' \ - ARG_SYSTEM_PROMPT='${base64encode(local.final_system_prompt)}' \ - ARG_AI_PROMPT='${base64encode(var.ai_prompt)}' \ - /tmp/start.sh - EOT +resource "coder_env" "aider_api_key" { + count = var.api_key != "" ? 1 : 0 + agent_id = var.agent_id + name = local.env_var_name + value = var.api_key +} - install_script = <<-EOT - #!/bin/bash - set -o errexit - set -o pipefail +module "coder_utils" { + source = "registry.coder.com/coder/coder-utils/coder" + version = "0.0.1" - echo -n '${base64encode(local.install_script)}' | base64 -d > /tmp/install.sh - chmod +x /tmp/install.sh - ARG_WORKDIR='${var.workdir}' \ - ARG_INSTALL_AIDER='${var.install_aider}' \ - ARG_REPORT_TASKS='${var.report_tasks}' \ - ARG_AIDER_CONFIG="$(echo -n '${base64encode(local.base_aider_config)}' | base64 -d)" \ - /tmp/install.sh - EOT + agent_id = var.agent_id + module_directory = "$HOME/${local.module_dir_name}" + display_name_prefix = "Aider" + icon = var.icon + pre_install_script = var.pre_install_script + post_install_script = var.post_install_script + install_script = local.install_script } +output "scripts" { + description = "Ordered list of coder exp sync names for the coder_script resources this module actually creates, in run order (pre_install, install, post_install). Scripts that were not configured are absent from the list." + value = module.coder_utils.scripts +} \ No newline at end of file diff --git a/registry/coder/modules/aider/main.tftest.hcl b/registry/coder/modules/aider/main.tftest.hcl index 281bde866..6eace2591 100644 --- a/registry/coder/modules/aider/main.tftest.hcl +++ b/registry/coder/modules/aider/main.tftest.hcl @@ -21,16 +21,6 @@ run "test_aider_basic" { condition = var.install_aider == true error_message = "install_aider should default to true" } - - assert { - condition = var.install_agentapi == true - error_message = "install_agentapi should default to true" - } - - assert { - condition = var.report_tasks == false - error_message = "report_tasks should default to false" - } } run "test_with_api_key" { @@ -55,14 +45,10 @@ run "test_custom_options" { variables { agent_id = "test-agent-789" workdir = "/home/coder/custom" - order = 5 - group = "development" icon = "/icon/custom.svg" model = "4o" ai_prompt = "Help me write better code" install_aider = false - install_agentapi = false - agentapi_version = "v0.10.0" api_key = "" base_aider_config = "read:\n - CONVENTIONS.md" } @@ -72,11 +58,6 @@ run "test_custom_options" { error_message = "Order variable should be set to 5" } - assert { - condition = var.group == "development" - error_message = "Group variable should be set to 'development'" - } - assert { condition = var.icon == "/icon/custom.svg" error_message = "Icon variable should be set to custom icon" @@ -96,16 +77,6 @@ run "test_custom_options" { condition = var.install_aider == false error_message = "install_aider should be set to false" } - - assert { - condition = var.install_agentapi == false - error_message = "install_agentapi should be set to false" - } - - assert { - condition = var.agentapi_version == "v0.10.0" - error_message = "AgentAPI version should be set to 'v0.10.0'" - } } run "test_with_scripts" { diff --git a/registry/coder/modules/aider/scripts/install.sh b/registry/coder/modules/aider/scripts/install.sh.tftpl similarity index 65% rename from registry/coder/modules/aider/scripts/install.sh rename to registry/coder/modules/aider/scripts/install.sh.tftpl index b2244aa01..5d7179d5b 100644 --- a/registry/coder/modules/aider/scripts/install.sh +++ b/registry/coder/modules/aider/scripts/install.sh.tftpl @@ -7,13 +7,14 @@ command_exists() { } # Inputs -ARG_WORKDIR=${ARG_WORKDIR:-/home/coder} -ARG_INSTALL_AIDER=${ARG_INSTALL_AIDER:-true} -ARG_AIDER_CONFIG=${ARG_AIDER_CONFIG:-} +ARG_WORKDIR='${ARG_WORKDIR}' +ARG_INSTALL_AIDER='${ARG_INSTALL_AIDER}' +ARG_AIDER_CONFIG='${ARG_AIDER_CONFIG}' echo "--------------------------------" -echo "Install flag: $ARG_INSTALL_AIDER" -echo "Workspace: $ARG_WORKDIR" +printf "ARG_WORKDIR: %s\n" "$${ARG_WORKDIR}" +printf "ARG_INSTALL_AIDER: %s\n" "$${ARG_INSTALL_AIDER}" +printf "ARG_AIDER_CONFIG: %s\n" "$${ARG_AIDER_CONFIG}" echo "--------------------------------" function install_aider() { @@ -21,8 +22,8 @@ function install_aider() { sudo apt-get install -y pipx echo "pipx installed!" pipx ensurepath - mkdir -p "$ARG_WORKDIR/.local/bin" - export PATH="$HOME/.local/bin:$ARG_WORKDIR/.local/bin:$PATH" + mkdir -p "$${ARG_WORKDIR}/.local/bin" + export PATH="$HOME/.local/bin:$${ARG_WORKDIR}/.local/bin:$PATH" if ! command_exists aider; then echo "Installing Aider via pipx..." @@ -33,12 +34,12 @@ function install_aider() { } function configure_aider_settings() { - if [ -n "${ARG_AIDER_CONFIG}" ]; then + if [ -n "$${ARG_AIDER_CONFIG}" ]; then echo "Configuring Aider environment variables and model" mkdir -p "$HOME/.config/aider" - echo "$ARG_AIDER_CONFIG" > "$HOME/.config/aider/.aider.conf.yml" + echo "$${ARG_AIDER_CONFIG}" > "$HOME/.config/aider/.aider.conf.yml" echo "Aider config created at $HOME/.config/aider/.aider.conf.yml" else printf "No Aider environment variables or model configured\n" diff --git a/registry/coder/modules/aider/scripts/start.sh b/registry/coder/modules/aider/scripts/start.sh deleted file mode 100644 index 1bd18ffa6..000000000 --- a/registry/coder/modules/aider/scripts/start.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/bash -set -euo pipefail - -# Ensure pipx-installed apps are in PATH -export PATH="$HOME/.local/bin:$PATH" - -ARG_WORKDIR=${ARG_WORKDIR:-/home/coder} -ARG_API_KEY=$(echo -n "${ARG_API_KEY:-}" | base64 -d) -ARG_SYSTEM_PROMPT=$(echo -n "${ARG_SYSTEM_PROMPT:-}" | base64 -d 2> /dev/null || echo "") -ARG_AI_PROMPT=$(echo -n "${ARG_AI_PROMPT:-}" | base64 -d 2> /dev/null || echo "") -ARG_MODEL=${ARG_MODEL:-} -ARG_PROVIDER=${ARG_PROVIDER:-} -ARG_ENV_API_NAME_HOLDER=${ARG_ENV_API_NAME_HOLDER:-} - -echo "--------------------------------" -echo "Provider: $ARG_PROVIDER" -echo "Model: $ARG_MODEL" -echo "--------------------------------" - -if [ -n "$ARG_API_KEY" ]; then - printf "API key provided!\n" - export $ARG_ENV_API_NAME_HOLDER=$ARG_API_KEY -else - printf "API key not provided.\n" -fi - -build_initial_prompt() { - local initial_prompt="" - if [ -n "$ARG_AI_PROMPT" ]; then - if [ -n "$ARG_SYSTEM_PROMPT" ]; then - initial_prompt="$ARG_SYSTEM_PROMPT $ARG_AI_PROMPT" - else - initial_prompt="$ARG_AI_PROMPT" - fi - fi - echo "$initial_prompt" -} - -start_agentapi() { - echo "Starting in directory: $ARG_WORKDIR" - cd "$ARG_WORKDIR" - - local initial_prompt - initial_prompt=$(build_initial_prompt) - if [ -n "$initial_prompt" ]; then - echo "Starting agentapi with initial prompt" - agentapi server -I="$initial_prompt" --type aider --term-width=67 --term-height=1190 -- aider --model $ARG_MODEL --yes-always - else - agentapi server --term-width=67 --term-height=1190 -- aider --model $ARG_MODEL --yes-always - fi -} - -# TODO: Implement MCP server for coder when Aider support MCP servers. - -start_agentapi