Skip to content
Draft
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
74 changes: 27 additions & 47 deletions registry/coder/modules/aider/README.md
Original file line number Diff line number Diff line change
@@ -1,68 +1,51 @@
---
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]
---

# 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
Expand All @@ -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
}
```
Expand All @@ -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)
36 changes: 2 additions & 34 deletions registry/coder/modules/aider/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand All @@ -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");
});
Expand Down
169 changes: 28 additions & 141 deletions registry/coder/modules/aider/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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."
Expand All @@ -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."
Expand All @@ -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" {
Expand All @@ -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.)"
Expand Down Expand Up @@ -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 ? "<system>\n${var.system_prompt}${local.task_reporting_prompt}\n</system>" : "<system>\n${var.system_prompt}\n</system>"
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 = {
Expand All @@ -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
}
Loading
Loading