diff --git a/.icons/aider.svg b/.icons/aider.svg new file mode 100644 index 0000000..44e064f --- /dev/null +++ b/.icons/aider.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/registry/coder/modules/aider/README.md b/registry/coder/modules/aider/README.md new file mode 100644 index 0000000..c1d8697 --- /dev/null +++ b/registry/coder/modules/aider/README.md @@ -0,0 +1,315 @@ +--- +display_name: Aider +description: Run Aider AI pair programming in your workspace +icon: ../../../../.icons/code.svg +maintainer_github: coder +verified: true +tags: [agent, aider] +--- + +# Aider + +Run [Aider](https://aider.chat) AI pair programming in your workspace. This module installs Aider and provides a persistent session using screen or tmux. + +```tf +module "aider" { + source = "registry.coder.com/coder/aider/coder" + version = "1.0.0" + agent_id = coder_agent.example.id +} +``` + +## Features + +- **Interactive Parameter Selection**: Choose your AI provider, model, and configuration options when creating the workspace +- **Multiple AI Providers**: Supports Anthropic (Claude), OpenAI, DeepSeek, GROQ, and OpenRouter +- **Persistent Sessions**: Uses screen (default) or tmux to keep Aider running in the background +- **Optional Dependencies**: Install Playwright for web page scraping and PortAudio for voice coding +- **Project Integration**: Works with any project directory, including Git repositories +- **Browser UI**: Use Aider in your browser with a modern web interface instead of the terminal +- **Non-Interactive Mode**: Automatically processes tasks when provided via the `task_prompt` variable + +## Module Parameters + +| Parameter | Description | Type | Default | +| ---------------------------------- | -------------------------------------------------------------------------- | -------- | ------------------- | +| `agent_id` | The ID of a Coder agent (required) | `string` | - | +| `folder` | The folder to run Aider in | `string` | `/home/coder` | +| `install_aider` | Whether to install Aider | `bool` | `true` | +| `aider_version` | The version of Aider to install | `string` | `"latest"` | +| `use_screen` | Whether to use screen for running Aider in the background | `bool` | `true` | +| `use_tmux` | Whether to use tmux instead of screen for running Aider in the background | `bool` | `false` | +| `session_name` | Name for the persistent session (screen or tmux) | `string` | `"aider"` | +| `order` | Position of the app in the UI presentation | `number` | `null` | +| `icon` | The icon to use for the app | `string` | `"/icon/aider.svg"` | +| `experiment_report_tasks` | Whether to enable task reporting | `bool` | `true` | +| `system_prompt` | System prompt for instructing Aider on task reporting and behavior | `string` | See default in code | +| `task_prompt` | Task prompt to use with Aider | `string` | `""` | +| `ai_provider` | AI provider to use with Aider (openai, anthropic, azure, etc.) | `string` | `"anthropic"` | +| `ai_model` | AI model to use (can use Aider's built-in aliases like "sonnet", "4o") | `string` | `"sonnet"` | +| `ai_api_key` | API key for the selected AI provider | `string` | `""` | +| `custom_env_var_name` | Custom environment variable name when using custom provider | `string` | `""` | +| `experiment_pre_install_script` | Custom script to run before installing Aider | `string` | `null` | +| `experiment_post_install_script` | Custom script to run after installing Aider | `string` | `null` | +| `experiment_additional_extensions` | Additional extensions configuration in YAML format to append to the config | `string` | `null` | + +> **Note**: `use_screen` and `use_tmux` cannot both be enabled at the same time. By default, `use_screen` is set to `true` and `use_tmux` is set to `false`. + +## Usage Examples + +### Basic setup with API key + +```tf +variable "anthropic_api_key" { + type = string + description = "Anthropic API key" + sensitive = true +} + +module "aider" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/aider/coder" + version = "1.0.0" + agent_id = coder_agent.example.id + ai_api_key = var.anthropic_api_key +} +``` + +This basic setup will: + +- Install Aider in the workspace +- Create a persistent screen session named "aider" +- Configure Aider to use Anthropic Claude 3.7 Sonnet model +- Enable task reporting (configures Aider to report tasks to Coder MCP) + +### Using OpenAI with tmux + +```tf +variable "openai_api_key" { + type = string + description = "OpenAI API key" + sensitive = true +} + +module "aider" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/aider/coder" + version = "1.0.0" + agent_id = coder_agent.example.id + use_tmux = true + ai_provider = "openai" + ai_model = "4o" # Uses Aider's built-in alias for gpt-4o + ai_api_key = var.openai_api_key +} +``` + +### Using a custom provider + +```tf +variable "custom_api_key" { + type = string + description = "Custom provider API key" + sensitive = true +} + +module "aider" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/aider/coder" + version = "1.0.0" + agent_id = coder_agent.example.id + ai_provider = "custom" + custom_env_var_name = "MY_CUSTOM_API_KEY" + ai_model = "custom-model" + ai_api_key = var.custom_api_key +} +``` + +### Adding Custom Extensions (Experimental) + +You can extend Aider's capabilities by adding custom extensions: + +```tf +module "aider" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/aider/coder" + version = "1.0.0" + agent_id = coder_agent.example.id + ai_api_key = var.anthropic_api_key + + experiment_pre_install_script = <<-EOT + pip install some-custom-dependency + EOT + + experiment_additional_extensions = <<-EOT + custom-extension: + args: [] + cmd: custom-extension-command + description: A custom extension for Aider + enabled: true + envs: {} + name: custom-extension + timeout: 300 + type: stdio + EOT +} +``` + +Note: The indentation in the heredoc is preserved, so you can write the YAML naturally. + +## Task Reporting (Experimental) + +> This functionality is in early access as of Coder v2.21 and is still evolving. +> For now, we recommend testing it in a demo or staging environment, +> rather than deploying to production +> +> Learn more in [the Coder documentation](https://coder.com/docs/tutorials/ai-agents) +> +> Join our [Discord channel](https://discord.gg/coder) or +> [contact us](https://coder.com/contact) to get help or share feedback. + +Your workspace must have either `screen` or `tmux` installed to use this. + +Task reporting is **enabled by default** in this module, allowing you to: + +- Send an initial prompt to Aider during workspace creation +- Monitor task progress in the Coder UI +- Use the `coder_parameter` resource to collect prompts from users + +### Setting up Task Reporting + +To use task reporting effectively: + +1. Add the Coder Login module to your template +2. Configure the necessary variables to pass the task prompt +3. Optionally add a coder_parameter to collect prompts from users + +Here's a complete example: + +```tf +module "coder-login" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/modules/coder-login/coder" + version = "1.0.15" + agent_id = coder_agent.example.id +} + +variable "anthropic_api_key" { + type = string + description = "Anthropic API key" + sensitive = true +} + +data "coder_parameter" "ai_prompt" { + type = "string" + name = "AI Prompt" + default = "" + description = "Write a prompt for Aider" + mutable = true + ephemeral = true +} + +module "aider" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/aider/coder" + version = "1.0.0" + agent_id = coder_agent.example.id + ai_api_key = var.anthropic_api_key + task_prompt = data.coder_parameter.ai_prompt.value + + # Optionally customize the system prompt + system_prompt = <<-EOT +You are a helpful Coding assistant. Aim to autonomously investigate +and solve issues the user gives you and test your work, whenever possible. +Avoid shortcuts like mocking tests. When you get stuck, you can ask the user +but opt for autonomy. +YOU MUST REPORT ALL TASKS TO CODER. +When reporting tasks, you MUST follow these EXACT instructions: +- IMMEDIATELY report status after receiving ANY user message. +- Be granular. If you are investigating with multiple steps, report each step to coder. +Task state MUST be one of the following: +- Use "state": "working" when actively processing WITHOUT needing additional user input. +- Use "state": "complete" only when finished with a task. +- Use "state": "failure" when you need ANY user input, lack sufficient details, or encounter blockers. +Task summaries MUST: +- Include specifics about what you're doing. +- Include clear and actionable steps for the user. +- Be less than 160 characters in length. + EOT +} +``` + +When a task prompt is provided via the `task_prompt` variable, the module automatically: + +1. Combines the system prompt with the task prompt into a single message in the format: + +``` +SYSTEM PROMPT: +[system_prompt content] + +This is your current task: [task_prompt] +``` + +2. Executes the task during workspace creation using the `--message` and `--yes-always` flags +3. Logs task output to `$HOME/.aider.log` for reference + +If you want to disable task reporting, set `experiment_report_tasks = false` in your module configuration. + +## Using Aider in Your Workspace + +After the workspace starts, Aider will be installed and configured according to your parameters. A persistent session will automatically be started during workspace creation. + +### Session Options + +You can run Aider in three different ways: + +1. **Direct Mode**: Aider starts directly in the specified folder when you click the app button + +- Simple setup without persistent context +- Suitable for quick coding sessions + +2. **Screen Mode** (Default): Run Aider in a screen session that persists across connections + +- Session name: "aider" (or configured via `session_name`) + +3. **Tmux Mode**: Run Aider in a tmux session instead of screen + +- Set `use_tmux = true` to enable +- Session name: "aider" (or configured via `session_name`) +- Configures tmux with mouse support for shared sessions + +Persistent sessions (screen/tmux) allow you to: + +- Disconnect and reconnect without losing context +- Run Aider in the background while doing other work +- Switch between terminal and browser interfaces + +### Available AI Providers and Models + +Aider supports various providers and models, and this module integrates directly with Aider's built-in model aliases: + +| Provider | Example Models/Aliases | Default Model | +| ------------- | --------------------------------------------- | ---------------------- | +| **anthropic** | "sonnet" (Claude 3.7 Sonnet), "opus", "haiku" | "sonnet" | +| **openai** | "4o" (GPT-4o), "4" (GPT-4), "3.5-turbo" | "4o" | +| **azure** | Azure OpenAI models | "gpt-4" | +| **google** | "gemini" (Gemini Pro), "gemini-2.5-pro" | "gemini-2.5-pro" | +| **cohere** | "command-r-plus", etc. | "command-r-plus" | +| **mistral** | "mistral-large-latest" | "mistral-large-latest" | +| **ollama** | "llama3", etc. | "llama3" | +| **custom** | Any model name with custom ENV variable | - | + +For a complete and up-to-date list of supported aliases and models, please refer to the [Aider LLM documentation](https://aider.chat/docs/llms.html) and the [Aider LLM Leaderboards](https://aider.chat/docs/leaderboards.html) which show performance comparisons across different models. + +## Troubleshooting + +If you encounter issues: + +1. **Screen/Tmux issues**: If you can't reconnect to your session, check if the session exists with `screen -list` or `tmux list-sessions` +2. **API key issues**: Ensure you've entered the correct API key for your selected provider +3. **Browser mode issues**: If the browser interface doesn't open, check that you're accessing it from a machine that can reach your Coder workspace + +For more information on using Aider, see the [Aider documentation](https://aider.chat/docs/). + +``` + +``` diff --git a/registry/coder/modules/aider/main.test.ts b/registry/coder/modules/aider/main.test.ts new file mode 100644 index 0000000..c25513a --- /dev/null +++ b/registry/coder/modules/aider/main.test.ts @@ -0,0 +1,107 @@ +import { describe, expect, it } from "bun:test"; +import { + findResourceInstance, + runTerraformApply, + runTerraformInit, + testRequiredVariables, +} from "~test"; + +describe("aider", async () => { + await runTerraformInit(import.meta.dir); + + testRequiredVariables(import.meta.dir, { + agent_id: "foo", + }); + + it("configures task prompt correctly", async () => { + const testPrompt = "Add a hello world function"; + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + task_prompt: testPrompt, + }); + + const instance = findResourceInstance(state, "coder_script"); + expect(instance.script).toContain( + `This is your current task: ${testPrompt}`, + ); + expect(instance.script).toContain("aider --architect --yes-always"); + }); + + it("handles custom system prompt", async () => { + const customPrompt = "Report all tasks with state: working"; + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + system_prompt: customPrompt, + }); + + const instance = findResourceInstance(state, "coder_script"); + expect(instance.script).toContain(customPrompt); + }); + + it("handles pre and post install scripts", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + experiment_pre_install_script: "echo 'Pre-install script executed'", + experiment_post_install_script: "echo 'Post-install script executed'", + }); + + const instance = findResourceInstance(state, "coder_script"); + + expect(instance.script).toContain("Running pre-install script"); + expect(instance.script).toContain("Running post-install script"); + expect(instance.script).toContain("base64 -d > /tmp/pre_install.sh"); + expect(instance.script).toContain("base64 -d > /tmp/post_install.sh"); + }); + + it("validates that use_screen and use_tmux cannot both be true", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + use_screen: true, + use_tmux: true, + }); + + const instance = findResourceInstance(state, "coder_script"); + + expect(instance.script).toContain( + "Error: Both use_screen and use_tmux cannot be enabled at the same time", + ); + expect(instance.script).toContain("exit 1"); + }); + + it("configures Aider with known provider and model", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + ai_provider: "anthropic", + ai_model: "sonnet", + ai_api_key: "test-anthropic-key", + }); + + const instance = findResourceInstance(state, "coder_script"); + expect(instance.script).toContain( + 'export ANTHROPIC_API_KEY=\\"test-anthropic-key\\"', + ); + expect(instance.script).toContain("--model sonnet"); + expect(instance.script).toContain( + "Starting Aider using anthropic provider and model: sonnet", + ); + }); + + it("handles custom provider with custom env var and API key", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + ai_provider: "custom", + custom_env_var_name: "MY_CUSTOM_API_KEY", + ai_model: "custom-model", + ai_api_key: "test-custom-key", + }); + + const instance = findResourceInstance(state, "coder_script"); + expect(instance.script).toContain( + 'export MY_CUSTOM_API_KEY=\\"test-custom-key\\"', + ); + expect(instance.script).toContain("--model custom-model"); + expect(instance.script).toContain( + "Starting Aider using custom provider and model: custom-model", + ); + }); +}); diff --git a/registry/coder/modules/aider/main.tf b/registry/coder/modules/aider/main.tf new file mode 100644 index 0000000..77639df --- /dev/null +++ b/registry/coder/modules/aider/main.tf @@ -0,0 +1,502 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + coder = { + source = "coder/coder" + version = ">= 0.17" + } + } +} + +variable "agent_id" { + type = string + description = "The ID of a Coder agent." +} + +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 "icon" { + type = string + description = "The icon to use for the app." + default = "/icon/aider.svg" +} + +variable "folder" { + type = string + description = "The folder to run Aider in." + default = "/home/coder" +} + +variable "install_aider" { + type = bool + description = "Whether to install Aider." + default = true +} + +variable "aider_version" { + type = string + description = "The version of Aider to install." + default = "latest" +} + +variable "use_screen" { + type = bool + description = "Whether to use screen for running Aider in the background" + default = true +} + +variable "use_tmux" { + type = bool + description = "Whether to use tmux instead of screen for running Aider in the background" + default = false +} + +variable "session_name" { + type = string + description = "Name for the persistent session (screen or tmux)" + default = "aider" +} + +variable "experiment_report_tasks" { + type = bool + description = "Whether to enable task reporting." + default = true +} + +variable "system_prompt" { + type = string + description = "System prompt for instructing Aider on task reporting and behavior" + default = <<-EOT +You are a helpful Coding assistant. Aim to autonomously investigate +and solve issues the user gives you and test your work, whenever possible. +Avoid shortcuts like mocking tests. When you get stuck, you can ask the user +but opt for autonomy. +YOU MUST REPORT ALL TASKS TO CODER. +When reporting tasks, you MUST follow these EXACT instructions: +- IMMEDIATELY report status after receiving ANY user message. +- Be granular. If you are investigating with multiple steps, report each step to coder. +Task state MUST be one of the following: +- Use "state": "working" when actively processing WITHOUT needing additional user input. +- Use "state": "complete" only when finished with a task. +- Use "state": "failure" when you need ANY user input, lack sufficient details, or encounter blockers. +Task summaries MUST: +- Include specifics about what you're doing. +- Include clear and actionable steps for the user. +- Be less than 160 characters in length. +EOT +} + +variable "task_prompt" { + type = string + description = "Task prompt to use with Aider" + default = "" +} + +variable "experiment_pre_install_script" { + type = string + description = "Custom script to run before installing Aider." + default = null +} + +variable "experiment_post_install_script" { + type = string + description = "Custom script to run after installing Aider." + default = null +} + +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.)" + default = "anthropic" + validation { + condition = contains(["openai", "anthropic", "azure", "google", "cohere", "mistral", "ollama", "custom"], var.ai_provider) + error_message = "ai_provider must be one of: openai, anthropic, azure, google, cohere, mistral, ollama, custom" + } +} + +variable "ai_model" { + type = string + description = "AI model to use with Aider. Can use Aider's built-in aliases like '4o' (gpt-4o), 'sonnet' (claude-3-7-sonnet), 'opus' (claude-3-opus), etc." + default = "sonnet" +} + +variable "ai_api_key" { + type = string + description = "API key for the selected AI provider. This will be set as the appropriate environment variable based on the provider." + default = "" + sensitive = true +} + +variable "custom_env_var_name" { + type = string + description = "Custom environment variable name when using custom provider" + default = "" +} + +locals { + base_extensions = <<-EOT +coder: + args: + - exp + - mcp + - server + cmd: coder + description: Report ALL tasks and statuses (in progress, done, failed) you are working on. + enabled: true + envs: + CODER_MCP_APP_STATUS_SLUG: aider + name: Coder + timeout: 3000 + type: stdio +developer: + display_name: Developer + enabled: true + name: developer + timeout: 300 + type: builtin +EOT + + formatted_base = " ${replace(trimspace(local.base_extensions), "\n", "\n ")}" + additional_extensions = var.experiment_additional_extensions != null ? "\n ${replace(trimspace(var.experiment_additional_extensions), "\n", "\n ")}" : "" + + combined_extensions = <<-EOT +extensions: +${local.formatted_base}${local.additional_extensions} +EOT + + encoded_pre_install_script = var.experiment_pre_install_script != null ? base64encode(var.experiment_pre_install_script) : "" + encoded_post_install_script = var.experiment_post_install_script != null ? base64encode(var.experiment_post_install_script) : "" + + # Combine system prompt and task prompt for aider + combined_prompt = trimspace(<<-EOT +SYSTEM PROMPT: +${var.system_prompt} + +This is your current task: ${var.task_prompt} +EOT + ) + + # Map providers to their environment variable names + provider_env_vars = { + openai = "OPENAI_API_KEY" + anthropic = "ANTHROPIC_API_KEY" + azure = "AZURE_OPENAI_API_KEY" + google = "GOOGLE_API_KEY" + cohere = "COHERE_API_KEY" + mistral = "MISTRAL_API_KEY" + ollama = "OLLAMA_HOST" + custom = var.custom_env_var_name + } + + # Get the environment variable name for selected provider + env_var_name = local.provider_env_vars[var.ai_provider] + + # Model flag for aider command + model_flag = var.ai_provider == "ollama" ? "--ollama-model" : "--model" +} + +# Install and Initialize Aider +resource "coder_script" "aider" { + agent_id = var.agent_id + display_name = "Aider" + icon = var.icon + script = <<-EOT + #!/bin/bash + set -e + + command_exists() { + command -v "$1" >/dev/null 2>&1 + } + + echo "Setting up Aider AI pair programming..." + + if [ "${var.use_screen}" = "true" ] && [ "${var.use_tmux}" = "true" ]; then + echo "Error: Both use_screen and use_tmux cannot be enabled at the same time." + exit 1 + fi + + mkdir -p "${var.folder}" + + if [ "$(uname)" = "Linux" ]; then + echo "Checking dependencies for Linux..." + + if [ "${var.use_tmux}" = "true" ]; then + if ! command_exists tmux; then + echo "Installing tmux for persistent sessions..." + if command -v apt-get >/dev/null 2>&1; then + if command -v sudo >/dev/null 2>&1; then + sudo apt-get update -qq + sudo apt-get install -y -qq tmux + else + apt-get update -qq || echo "Warning: Cannot update package lists without sudo privileges" + apt-get install -y -qq tmux || echo "Warning: Cannot install tmux without sudo privileges" + fi + elif command -v dnf >/dev/null 2>&1; then + if command -v sudo >/dev/null 2>&1; then + sudo dnf install -y -q tmux + else + dnf install -y -q tmux || echo "Warning: Cannot install tmux without sudo privileges" + fi + else + echo "Warning: Unable to install tmux on this system. Neither apt-get nor dnf found." + fi + else + echo "tmux is already installed, skipping installation." + fi + elif [ "${var.use_screen}" = "true" ]; then + if ! command_exists screen; then + echo "Installing screen for persistent sessions..." + if command -v apt-get >/dev/null 2>&1; then + if command -v sudo >/dev/null 2>&1; then + sudo apt-get update -qq + sudo apt-get install -y -qq screen + else + apt-get update -qq || echo "Warning: Cannot update package lists without sudo privileges" + apt-get install -y -qq screen || echo "Warning: Cannot install screen without sudo privileges" + fi + elif command -v dnf >/dev/null 2>&1; then + if command -v sudo >/dev/null 2>&1; then + sudo dnf install -y -q screen + else + dnf install -y -q screen || echo "Warning: Cannot install screen without sudo privileges" + fi + else + echo "Warning: Unable to install screen on this system. Neither apt-get nor dnf found." + fi + else + echo "screen is already installed, skipping installation." + fi + fi + else + echo "This module currently only supports Linux workspaces." + exit 1 + fi + + if [ -n "${local.encoded_pre_install_script}" ]; then + echo "Running pre-install script..." + echo "${local.encoded_pre_install_script}" | base64 -d > /tmp/pre_install.sh + chmod +x /tmp/pre_install.sh + /tmp/pre_install.sh + fi + + if [ "${var.install_aider}" = "true" ]; then + echo "Installing Aider..." + + if ! command_exists python3 || ! command_exists pip3; then + echo "Installing Python dependencies required for Aider..." + if command -v apt-get >/dev/null 2>&1; then + if command -v sudo >/dev/null 2>&1; then + sudo apt-get update -qq + sudo apt-get install -y -qq python3-pip python3-venv + else + apt-get update -qq || echo "Warning: Cannot update package lists without sudo privileges" + apt-get install -y -qq python3-pip python3-venv || echo "Warning: Cannot install Python packages without sudo privileges" + fi + elif command -v dnf >/dev/null 2>&1; then + if command -v sudo >/dev/null 2>&1; then + sudo dnf install -y -q python3-pip python3-virtualenv + else + dnf install -y -q python3-pip python3-virtualenv || echo "Warning: Cannot install Python packages without sudo privileges" + fi + else + echo "Warning: Unable to install Python on this system. Neither apt-get nor dnf found." + fi + else + echo "Python is already installed, skipping installation." + fi + + if ! command_exists aider; then + curl -LsSf https://aider.chat/install.sh | sh + fi + + if [ -f "$HOME/.bashrc" ]; then + if ! grep -q 'export PATH="$HOME/bin:$PATH"' "$HOME/.bashrc"; then + echo 'export PATH="$HOME/bin:$PATH"' >> "$HOME/.bashrc" + fi + fi + + if [ -f "$HOME/.zshrc" ]; then + if ! grep -q 'export PATH="$HOME/bin:$PATH"' "$HOME/.zshrc"; then + echo 'export PATH="$HOME/bin:$PATH"' >> "$HOME/.zshrc" + fi + fi + + fi + + if [ -n "${local.encoded_post_install_script}" ]; then + echo "Running post-install script..." + echo "${local.encoded_post_install_script}" | base64 -d > /tmp/post_install.sh + chmod +x /tmp/post_install.sh + /tmp/post_install.sh + fi + + if [ "${var.experiment_report_tasks}" = "true" ]; then + echo "Configuring Aider to report tasks via Coder MCP..." + + mkdir -p "$HOME/.config/aider" + + cat > "$HOME/.config/aider/config.yml" << EOL +${trimspace(local.combined_extensions)} +EOL + echo "Added Coder MCP extension to Aider config.yml" + fi + + echo "Starting persistent Aider session..." + + touch "$HOME/.aider.log" + + export LANG=en_US.UTF-8 + export LC_ALL=en_US.UTF-8 + + export PATH="$HOME/bin:$PATH" + + if [ "${var.use_tmux}" = "true" ]; then + if [ -n "${var.task_prompt}" ]; then + echo "Running Aider with message in tmux session..." + + # Configure tmux for shared sessions + if [ ! -f "$HOME/.tmux.conf" ]; then + echo "Creating ~/.tmux.conf with shared session settings..." + echo "set -g mouse on" > "$HOME/.tmux.conf" + fi + + if ! grep -q "^set -g mouse on$" "$HOME/.tmux.conf"; then + echo "Adding 'set -g mouse on' to ~/.tmux.conf..." + echo "set -g mouse on" >> "$HOME/.tmux.conf" + fi + + echo "Starting Aider using ${var.ai_provider} provider and model: ${var.ai_model}" + tmux new-session -d -s ${var.session_name} -c ${var.folder} "export ${local.env_var_name}=\"${var.ai_api_key}\"; aider --architect --yes-always ${local.model_flag} ${var.ai_model} --message \"${local.combined_prompt}\"" + echo "Aider task started in tmux session '${var.session_name}'. Check the UI for progress." + else + # Configure tmux for shared sessions + if [ ! -f "$HOME/.tmux.conf" ]; then + echo "Creating ~/.tmux.conf with shared session settings..." + echo "set -g mouse on" > "$HOME/.tmux.conf" + fi + + if ! grep -q "^set -g mouse on$" "$HOME/.tmux.conf"; then + echo "Adding 'set -g mouse on' to ~/.tmux.conf..." + echo "set -g mouse on" >> "$HOME/.tmux.conf" + fi + + echo "Starting Aider using ${var.ai_provider} provider and model: ${var.ai_model}" + tmux new-session -d -s ${var.session_name} -c ${var.folder} "export ${local.env_var_name}=\"${var.ai_api_key}\"; aider --architect --yes-always ${local.model_flag} ${var.ai_model} --message \"${var.system_prompt}\"" + echo "Tmux session '${var.session_name}' started. Access it by clicking the Aider button." + fi + else + if [ -n "${var.task_prompt}" ]; then + echo "Running Aider with message in screen session..." + + if [ ! -f "$HOME/.screenrc" ]; then + echo "Creating ~/.screenrc and adding multiuser settings..." + echo -e "multiuser on\nacladd $(whoami)" > "$HOME/.screenrc" + fi + + if ! grep -q "^multiuser on$" "$HOME/.screenrc"; then + echo "Adding 'multiuser on' to ~/.screenrc..." + echo "multiuser on" >> "$HOME/.screenrc" + fi + + if ! grep -q "^acladd $(whoami)$" "$HOME/.screenrc"; then + echo "Adding 'acladd $(whoami)' to ~/.screenrc..." + echo "acladd $(whoami)" >> "$HOME/.screenrc" + fi + + echo "Starting Aider using ${var.ai_provider} provider and model: ${var.ai_model}" + screen -U -dmS ${var.session_name} bash -c " + cd ${var.folder} + export PATH=\"$HOME/bin:$HOME/.local/bin:$PATH\" + export ${local.env_var_name}=\"${var.ai_api_key}\" + aider --architect --yes-always ${local.model_flag} ${var.ai_model} --message \"${local.combined_prompt}\" + /bin/bash + " + + echo "Aider task started in screen session '${var.session_name}'. Check the UI for progress." + else + + if [ ! -f "$HOME/.screenrc" ]; then + echo "Creating ~/.screenrc and adding multiuser settings..." + echo -e "multiuser on\nacladd $(whoami)" > "$HOME/.screenrc" + fi + + if ! grep -q "^multiuser on$" "$HOME/.screenrc"; then + echo "Adding 'multiuser on' to ~/.screenrc..." + echo "multiuser on" >> "$HOME/.screenrc" + fi + + if ! grep -q "^acladd $(whoami)$" "$HOME/.screenrc"; then + echo "Adding 'acladd $(whoami)' to ~/.screenrc..." + echo "acladd $(whoami)" >> "$HOME/.screenrc" + fi + + echo "Starting Aider using ${var.ai_provider} provider and model: ${var.ai_model}" + screen -U -dmS ${var.session_name} bash -c " + cd ${var.folder} + export PATH=\"$HOME/bin:$HOME/.local/bin:$PATH\" + export ${local.env_var_name}=\"${var.ai_api_key}\" + aider --architect --yes-always ${local.model_flag} ${var.ai_model} --message \"${local.combined_prompt}\" + /bin/bash + " + echo "Screen session '${var.session_name}' started. Access it by clicking the Aider button." + fi + fi + + echo "Aider setup complete!" + EOT + run_on_start = true +} + +# Aider CLI app +resource "coder_app" "aider_cli" { + agent_id = var.agent_id + slug = "aider" + display_name = "Aider" + icon = var.icon + command = <<-EOT + #!/bin/bash + set -e + + export PATH="$HOME/bin:$HOME/.local/bin:$PATH" + + export LANG=en_US.UTF-8 + export LC_ALL=en_US.UTF-8 + + if [ "${var.use_tmux}" = "true" ]; then + if tmux has-session -t ${var.session_name} 2>/dev/null; then + echo "Attaching to existing Aider tmux session..." + tmux attach-session -t ${var.session_name} + else + echo "Starting new Aider tmux session..." + tmux new-session -s ${var.session_name} -c ${var.folder} "export ${local.env_var_name}=\"${var.ai_api_key}\"; aider ${local.model_flag} ${var.ai_model} --message \"${local.combined_prompt}\"; exec bash" + fi + elif [ "${var.use_screen}" = "true" ]; then + if ! screen -list | grep -q "${var.session_name}"; then + echo "Error: No existing Aider session found. Please wait for the script to start it." + exit 1 + fi + screen -xRR ${var.session_name} + else + cd "${var.folder}" + echo "Starting Aider directly..." + export ${local.env_var_name}="${var.ai_api_key}" + aider ${local.model_flag} ${var.ai_model} --message "${local.combined_prompt}" + fi + EOT + order = var.order +}