diff --git a/.icons/auggie.svg b/.icons/auggie.svg new file mode 100644 index 000000000..590bd5aa1 --- /dev/null +++ b/.icons/auggie.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/registry/coder-labs/modules/auggie/README.md b/registry/coder-labs/modules/auggie/README.md new file mode 100644 index 000000000..4b8e93151 --- /dev/null +++ b/registry/coder-labs/modules/auggie/README.md @@ -0,0 +1,161 @@ +--- +display_name: Auggie CLI +icon: ../../../../.icons/auggie.svg +description: Run Auggie CLI in your workspace for AI-powered coding assistance with AgentAPI integration +verified: true +tags: [agent, auggie, ai, tasks, augment] +--- + +# Auggie CLI + +Run Auggie CLI in your workspace to access Augment's AI coding assistant with advanced context understanding and codebase integration. This module integrates with [AgentAPI](https://github.com/coder/agentapi). + +```tf +module "auggie" { + source = "registry.coder.com/coder-labs/auggie/coder" + version = "0.1.0" + agent_id = coder_agent.example.id + folder = "/home/coder/project" +} +``` + +## Prerequisites + +- **Node.js and npm must be sourced/available before the auggie module installs** - ensure they are installed in your workspace image or via earlier provisioning steps +- You must add the [Coder Login](https://registry.coder.com/modules/coder/coder-login) module to your template +- **Augment session token for authentication (required for tasks). [Instructions](https://docs.augmentcode.com/cli/setup-auggie/authentication) to get the session token** + +## Examples + +### Usage with Tasks and Configuration + +```tf +data "coder_parameter" "ai_prompt" { + type = "string" + name = "AI Prompt" + default = "" + description = "Initial task prompt for Auggie CLI" + mutable = true +} + +module "coder-login" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/coder-login/coder" + version = "1.0.31" + agent_id = coder_agent.example.id +} + +module "auggie" { + source = "registry.coder.com/coder-labs/auggie/coder" + version = "0.1.0" + agent_id = coder_agent.example.id + folder = "/home/coder/project" + + # Authentication + augment_session_token = <<-EOF + {"accessToken":"xxxx-yyyy-zzzz-jjjj","tenantURL":"https://d1.api.augmentcode.com/","scopes":["read","write"]} +EOF # Required for tasks + + # Version + auggie_version = "0.3.0" + + # Task configuration + ai_prompt = data.coder_parameter.ai_prompt.value + continue_previous_conversation = true + interaction_mode = "quiet" + auggie_model = "gpt5" + report_tasks = true + + # MCP configuration for additional integrations + mcp = <<-EOF +{ + "mcpServers": { + "filesystem": { + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-filesystem", "/home/coder/project"] + } + } +} +EOF + + # Workspace guidelines + rules = <<-EOT + # Project Guidelines + + ## Code Style + - Use TypeScript for all new JavaScript files + - Follow consistent naming conventions + - Add comprehensive comments for complex logic + + ## Testing + - Write unit tests for all new functions + - Ensure test coverage above 80% + + ## Documentation + - Update README.md for any new features + - Document API changes in CHANGELOG.md + EOT +} +``` + +### Using Multiple MCP Configuration Files + +```tf +module "auggie" { + source = "registry.coder.com/coder-labs/auggie/coder" + version = "0.1.0" + agent_id = coder_agent.example.id + folder = "/home/coder/project" + + # Multiple MCP configuration files + mcp_files = [ + "/path/to/filesystem-mcp.json", + "/path/to/database-mcp.json", + "/path/to/api-mcp.json" + ] + + mcp = <<-EOF + { + "mcpServers": { + "Test MCP": { + "command": "uv", + "args": [ + "--directory", + "/home/coder/test-mcp", + "run", + "server.py" + ], + "timeout": 600 + } + } +} +EOF +} +``` + +### Troubleshooting + +If you have any issues, please take a look at the log files below. + +```bash +# Installation logs +cat ~/.auggie-module/install.log + +# Startup logs +cat ~/.auggie-module/agentapi-start.log + +# Pre/post install script logs +cat ~/.auggie-module/pre_install.log +cat ~/.auggie-module/post_install.log +``` + +> [!NOTE] +> To use tasks with Auggie CLI, create a `coder_parameter` named `"AI Prompt"` and pass its value to the auggie module's `ai_prompt` variable. The `folder` variable is required for the module to function correctly. + +## References + +- [Auggie CLI Reference](https://docs.augmentcode.com/cli/reference) +- [Auggie CLI MCP Integration](https://docs.augmentcode.com/cli/integrations#mcp-integrations) +- [Augment Code Documentation](https://docs.augmentcode.com/) +- [AgentAPI Documentation](https://github.com/coder/agentapi) +- [Coder AI Agents Guide](https://coder.com/docs/tutorials/ai-agents) diff --git a/registry/coder-labs/modules/auggie/auggie.tftest.hcl b/registry/coder-labs/modules/auggie/auggie.tftest.hcl new file mode 100644 index 000000000..eae010799 --- /dev/null +++ b/registry/coder-labs/modules/auggie/auggie.tftest.hcl @@ -0,0 +1,186 @@ +run "test_auggie_basic" { + command = plan + + variables { + agent_id = "test-agent-123" + folder = "/home/coder/projects" + } + + assert { + condition = coder_env.auggie_session_auth.name == "AUGMENT_SESSION_AUTH" + error_message = "Auggie session auth environment variable should be set correctly" + } + + assert { + condition = var.folder == "/home/coder/projects" + error_message = "Folder variable should be set correctly" + } + + assert { + condition = var.agent_id == "test-agent-123" + error_message = "Agent ID variable should be set correctly" + } + + assert { + condition = var.install_auggie == true + error_message = "Install auggie should default to true" + } + + assert { + condition = var.install_agentapi == true + error_message = "Install agentapi should default to true" + } +} + +run "test_auggie_with_session_token" { + command = plan + + variables { + agent_id = "test-agent-456" + folder = "/home/coder/workspace" + augment_session_token = "test-session-token-123" + } + + assert { + condition = coder_env.auggie_session_auth.value == "test-session-token-123" + error_message = "Auggie session token value should match the input" + } +} + +run "test_auggie_with_custom_options" { + command = plan + + variables { + agent_id = "test-agent-789" + folder = "/home/coder/custom" + order = 5 + group = "development" + icon = "/icon/custom.svg" + auggie_model = "gpt-4" + ai_prompt = "Help me write better code" + interaction_mode = "compact" + continue_previous_conversation = true + install_auggie = false + install_agentapi = false + auggie_version = "1.0.0" + agentapi_version = "v0.6.0" + } + + assert { + condition = var.order == 5 + 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" + } + + assert { + condition = var.auggie_model == "gpt-4" + error_message = "Auggie model variable should be set to 'gpt-4'" + } + + assert { + condition = var.ai_prompt == "Help me write better code" + error_message = "AI prompt variable should be set correctly" + } + + assert { + condition = var.interaction_mode == "compact" + error_message = "Interaction mode should be set to 'compact'" + } + + assert { + condition = var.continue_previous_conversation == true + error_message = "Continue previous conversation should be set to true" + } + + assert { + condition = var.auggie_version == "1.0.0" + error_message = "Auggie version should be set to '1.0.0'" + } + + assert { + condition = var.agentapi_version == "v0.6.0" + error_message = "AgentAPI version should be set to 'v0.6.0'" + } +} + +run "test_auggie_with_mcp_and_rules" { + command = plan + + variables { + agent_id = "test-agent-mcp" + folder = "/home/coder/mcp-test" + mcp = jsonencode({ + mcpServers = { + test = { + command = "test-server" + args = ["--config", "test.json"] + } + } + }) + mcp_files = [ + "/path/to/mcp1.json", + "/path/to/mcp2.json" + ] + rules = "# General coding rules\n- Write clean code\n- Add comments" + } + + assert { + condition = var.mcp != "" + error_message = "MCP configuration should be provided" + } + + assert { + condition = length(var.mcp_files) == 2 + error_message = "Should have 2 MCP files" + } + + assert { + condition = var.rules != "" + error_message = "Rules should be provided" + } +} + +run "test_auggie_with_scripts" { + command = plan + + variables { + agent_id = "test-agent-scripts" + folder = "/home/coder/scripts" + pre_install_script = "echo 'Pre-install script'" + post_install_script = "echo 'Post-install script'" + } + + assert { + condition = var.pre_install_script == "echo 'Pre-install script'" + error_message = "Pre-install script should be set correctly" + } + + assert { + condition = var.post_install_script == "echo 'Post-install script'" + error_message = "Post-install script should be set correctly" + } +} + +run "test_auggie_interaction_mode_validation" { + command = plan + + variables { + agent_id = "test-agent-validation" + folder = "/home/coder/test" + interaction_mode = "print" + } + + assert { + condition = contains(["interactive", "print", "quiet", "compact"], var.interaction_mode) + error_message = "Interaction mode should be one of the valid options" + } +} diff --git a/registry/coder-labs/modules/auggie/main.test.ts b/registry/coder-labs/modules/auggie/main.test.ts new file mode 100644 index 000000000..185b933a8 --- /dev/null +++ b/registry/coder-labs/modules/auggie/main.test.ts @@ -0,0 +1,342 @@ +import { + test, + afterEach, + describe, + setDefaultTimeout, + beforeAll, + expect, +} from "bun:test"; +import { execContainer, readFileContainer, runTerraformInit } from "~test"; +import { + loadTestFile, + writeExecutable, + setup as setupUtil, + execModuleScript, + expectAgentAPIStarted, +} from "../../../coder/modules/agentapi/test-util"; +import dedent from "dedent"; + +let cleanupFunctions: (() => Promise)[] = []; +const registerCleanup = (cleanup: () => Promise) => { + cleanupFunctions.push(cleanup); +}; +afterEach(async () => { + const cleanupFnsCopy = cleanupFunctions.slice().reverse(); + cleanupFunctions = []; + for (const cleanup of cleanupFnsCopy) { + try { + await cleanup(); + } catch (error) { + console.error("Error during cleanup:", error); + } + } +}); + +interface SetupProps { + skipAgentAPIMock?: boolean; + skipAuggieMock?: boolean; + moduleVariables?: Record; + agentapiMockScript?: string; +} + +const setup = async (props?: SetupProps): Promise<{ id: string }> => { + const projectDir = "/home/coder/project"; + const { id } = await setupUtil({ + moduleDir: import.meta.dir, + moduleVariables: { + install_auggie: props?.skipAuggieMock ? "true" : "false", + install_agentapi: props?.skipAgentAPIMock ? "true" : "false", + folder: projectDir, + ...props?.moduleVariables, + }, + registerCleanup, + projectDir, + skipAgentAPIMock: props?.skipAgentAPIMock, + agentapiMockScript: props?.agentapiMockScript, + }); + if (!props?.skipAuggieMock) { + await writeExecutable({ + containerId: id, + filePath: "/usr/bin/auggie", + content: await loadTestFile(import.meta.dir, "auggie-mock.sh"), + }); + } + return { id }; +}; + +setDefaultTimeout(60 * 1000); + +describe("auggie", async () => { + beforeAll(async () => { + await runTerraformInit(import.meta.dir); + }); + + test("happy-path", async () => { + const { id } = await setup(); + await execModuleScript(id); + await expectAgentAPIStarted(id); + }); + + test("install-auggie-version", async () => { + const version_to_install = "0.3.0"; + const { id } = await setup({ + skipAuggieMock: true, + moduleVariables: { + install_auggie: "true", + auggie_version: version_to_install, + pre_install_script: dedent` + #!/usr/bin/env bash + set -euo pipefail + + # Install Node.js and npm via system package manager + if ! command -v node >/dev/null 2>&1; then + sudo apt-get update + sudo apt-get install -y nodejs npm + fi + + # Configure npm to use user directory (avoids permission issues) + mkdir -p "$HOME/.npm-global" + npm config set prefix "$HOME/.npm-global" + + # Persist npm user directory configuration + echo 'export PATH="$HOME/.npm-global/bin:$PATH"' >> ~/.bashrc + echo "prefix=$HOME/.npm-global" > ~/.npmrc + `, + }, + }); + await execModuleScript(id); + const resp = await execContainer(id, [ + "bash", + "-c", + `cat /home/coder/.auggie-module/install.log`, + ]); + expect(resp.stdout).toContain(version_to_install); + }); + + test("check-latest-auggie-version-works", async () => { + const { id } = await setup({ + skipAuggieMock: true, + skipAgentAPIMock: true, + moduleVariables: { + install_auggie: "true", + pre_install_script: dedent` + #!/usr/bin/env bash + set -euo pipefail + + # Install Node.js and npm via system package manager + if ! command -v node >/dev/null 2>&1; then + sudo apt-get update + sudo apt-get install -y nodejs npm + fi + + # Configure npm to use user directory (avoids permission issues) + mkdir -p "$HOME/.npm-global" + npm config set prefix "$HOME/.npm-global" + + # Persist npm user directory configuration + echo 'export PATH="$HOME/.npm-global/bin:$PATH"' >> ~/.bashrc + echo "prefix=$HOME/.npm-global" > ~/.npmrc + `, + }, + }); + await execModuleScript(id); + await expectAgentAPIStarted(id); + }); + + test("auggie-session-token", async () => { + const sessionToken = "test-session-token-123"; + const { id } = await setup({ + moduleVariables: { + augment_session_token: sessionToken, + }, + }); + await execModuleScript(id); + + const envCheck = await execContainer(id, [ + "bash", + "-c", + `env | grep AUGMENT_SESSION_AUTH || echo "AUGMENT_SESSION_AUTH not found"`, + ]); + expect(envCheck.stdout).toContain("AUGMENT_SESSION_AUTH"); + }); + + test("auggie-mcp-config", async () => { + const mcpConfig = JSON.stringify({ + mcpServers: { + test: { + command: "test-cmd", + type: "stdio" + } + } + }); + const { id } = await setup({ + moduleVariables: { + mcp: mcpConfig, + }, + }); + await execModuleScript(id); + + const resp = await readFileContainer( + id, + "/home/coder/.auggie-module/agentapi-start.log", + ); + expect(resp).toContain("--mcp-config"); + }); + + test("auggie-rules", async () => { + const rules = "Always use TypeScript for new files"; + const { id } = await setup({ + moduleVariables: { + install_auggie: "false", // Don't need to install auggie to test rules file creation + rules: rules, + }, + }); + await execModuleScript(id); + + const rulesFile = await readFileContainer(id, "/home/coder/.augment/rules.md"); + expect(rulesFile).toContain(rules); + }); + + test("auggie-ai-task-prompt", async () => { + const prompt = "This is a task prompt for Auggie."; + const { id } = await setup({ + moduleVariables: { + ai_prompt: prompt, + }, + }); + await execModuleScript(id); + + const resp = await execContainer(id, [ + "bash", + "-c", + `cat /home/coder/.auggie-module/agentapi-start.log`, + ]); + expect(resp.stdout).toContain(prompt); + }); + + test("auggie-interaction-mode", async () => { + const mode = "compact"; + const { id } = await setup({ + moduleVariables: { + interaction_mode: mode, + ai_prompt: "test prompt", + }, + }); + await execModuleScript(id); + + const startLog = await execContainer(id, [ + "bash", + "-c", + "cat /home/coder/.auggie-module/agentapi-start.log", + ]); + expect(startLog.stdout).toContain(`--${mode}`); + }); + + test("auggie-model", async () => { + const model = "gpt-4"; + const { id } = await setup({ + moduleVariables: { + auggie_model: model, + ai_prompt: "test prompt", + }, + }); + await execModuleScript(id); + + const startLog = await execContainer(id, [ + "bash", + "-c", + "cat /home/coder/.auggie-module/agentapi-start.log", + ]); + expect(startLog.stdout).toContain(`--model ${model}`); + }); + + test("auggie-continue-previous-conversation", async () => { + const { id } = await setup({ + moduleVariables: { + continue_previous_conversation: "true", + ai_prompt: "test prompt", + }, + }); + await execModuleScript(id); + + const startLog = await execContainer(id, [ + "bash", + "-c", + "cat /home/coder/.auggie-module/agentapi-start.log", + ]); + expect(startLog.stdout).toContain("--continue"); + }); + + test("pre-post-install-scripts", async () => { + const { id } = await setup({ + moduleVariables: { + pre_install_script: "#!/bin/bash\necho 'auggie-pre-install-script'", + post_install_script: "#!/bin/bash\necho 'auggie-post-install-script'", + }, + }); + await execModuleScript(id); + + const preInstallLog = await readFileContainer( + id, + "/home/coder/.auggie-module/pre_install.log", + ); + expect(preInstallLog).toContain("auggie-pre-install-script"); + + const postInstallLog = await readFileContainer( + id, + "/home/coder/.auggie-module/post_install.log", + ); + expect(postInstallLog).toContain("auggie-post-install-script"); + }); + + test("folder-variable", async () => { + const folder = "/home/coder/auggie-test-folder"; + const { id } = await setup({ + skipAuggieMock: false, + moduleVariables: { + folder, + }, + }); + await execModuleScript(id); + + const resp = await readFileContainer( + id, + "/home/coder/.auggie-module/agentapi-start.log", + ); + expect(resp).toContain(folder); + }); + + test("coder-mcp-config-created", async () => { + const { id } = await setup({ + moduleVariables: { + install_auggie: "false", // Don't need to install auggie to test MCP config creation + }, + }); + await execModuleScript(id); + + const mcpConfig = await readFileContainer(id, "/home/coder/.augment/coder_mcp.json"); + expect(mcpConfig).toContain("mcpServers"); + expect(mcpConfig).toContain("coder"); + expect(mcpConfig).toContain("CODER_MCP_APP_STATUS_SLUG"); + expect(mcpConfig).toContain("CODER_MCP_AI_AGENTAPI_URL"); + }); + + test("mcp-files-array", async () => { + const mcpFiles = ["/path/to/mcp1.json", "/path/to/mcp2.json"]; + const { id } = await setup({ + moduleVariables: { + mcp_files: JSON.stringify(mcpFiles), + ai_prompt: "test prompt", + }, + }); + await execModuleScript(id); + + const startLog = await execContainer(id, [ + "bash", + "-c", + "cat /home/coder/.auggie-module/agentapi-start.log", + ]); + expect(startLog.stdout).toContain("mcp1.json"); + expect(startLog.stdout).toContain("mcp2.json"); + }); +}); diff --git a/registry/coder-labs/modules/auggie/main.tf b/registry/coder-labs/modules/auggie/main.tf new file mode 100644 index 000000000..d6c326e0b --- /dev/null +++ b/registry/coder-labs/modules/auggie/main.tf @@ -0,0 +1,230 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + coder = { + source = "coder/coder" + version = ">= 2.7" + } + } +} + +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 "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." + default = "/icon/auggie.svg" +} + +variable "folder" { + type = string + description = "The folder to run Auggie in." +} + +variable "install_auggie" { + type = bool + description = "Whether to install Auggie CLI." + default = true +} + +variable "auggie_version" { + type = string + description = "The version of Auggie to install." + default = "" # empty string means the latest available version + validation { + condition = var.auggie_version == "" || can(regex("^v?[0-9]+\\.[0-9]+\\.[0-9]+", var.auggie_version)) + error_message = "auggie_version must be empty (for latest) or a valid semantic version like 'v1.2.3' or '1.2.3'." + } +} + +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.6.0" + validation { + condition = can(regex("^v[0-9]+\\.[0-9]+\\.[0-9]+", var.agentapi_version)) + error_message = "agentapi_version must be a valid semantic version starting with 'v', like 'v0.3.3'." + } +} + +variable "pre_install_script" { + type = string + description = "Custom script to run before installing Auggie." + default = null +} + +variable "post_install_script" { + type = string + description = "Custom script to run after installing Auggie." + default = null +} + +# ---------------------------------------------- + +variable "ai_prompt" { + type = string + description = "Task prompt for the Auggie CLI" + default = "" +} + +variable "mcp" { + type = string + description = "MCP configuration as a JSON string for the auggie cli, check https://docs.augmentcode.com/cli/integrations#mcp-integrations" + default = "" +} + +variable "mcp_files" { + type = list(string) + description = "MCP configuration from a JSON file for the auggie cli, check https://docs.augmentcode.com/cli/integrations#mcp-integrations" + default = [] +} + +variable "rules" { + type = string + description = "Additional rules to append to workspace guidelines (markdown format)" + default = "" +} + +variable "continue_previous_conversation" { + type = bool + description = "Whether to resume the previous conversation." + default = false +} + +variable "interaction_mode" { + type = string + description = "Interaction mode with the Auggie CLI. Options: interactive, print, quiet, compact. https://docs.augmentcode.com/cli/reference#cli-flags" + default = "interactive" + validation { + condition = contains(["interactive", "print", "quiet", "compact"], var.interaction_mode) + error_message = "interaction_mode must be one of: interactive, print, quiet, compact." + } +} + +variable "augment_session_token" { + type = string + description = "Auggie session token for authentication. https://docs.augmentcode.com/cli/setup-auggie/authentication" + default = "" +} + +variable "auggie_model" { + type = string + description = "The model to use for Auggie, find available models using auggie --list-models" + default = "" +} + +variable "report_tasks" { + type = bool + description = "Whether to enable task reporting to Coder UI via AgentAPI" + default = false +} + +variable "cli_app" { + type = bool + description = "Whether to create a CLI app for Auggie" + default = false +} + +variable "web_app_display_name" { + type = string + description = "Display name for the web app" + default = "Auggie" +} + +variable "cli_app_display_name" { + type = string + description = "Display name for the CLI app" + default = "Auggie CLI" +} + +resource "coder_env" "auggie_session_auth" { + agent_id = var.agent_id + name = "AUGMENT_SESSION_AUTH" + value = var.augment_session_token +} + +locals { + app_slug = "auggie" + install_script = file("${path.module}/scripts/install.sh") + start_script = file("${path.module}/scripts/start.sh") + module_dir_name = ".auggie-module" +} + +module "agentapi" { + source = "registry.coder.com/coder/agentapi/coder" + version = "1.1.1" + + 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 + 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_AUGGIE_START_DIRECTORY='${var.folder}' \ + ARG_TASK_PROMPT='${base64encode(var.ai_prompt)}' \ + ARG_MCP_FILES='${jsonencode(var.mcp_files)}' \ + ARG_AUGGIE_RULES='${base64encode(var.rules)}' \ + ARG_AUGGIE_CONTINUE_PREVIOUS_CONVERSATION='${var.continue_previous_conversation}' \ + ARG_AUGGIE_INTERACTION_MODE='${var.interaction_mode}' \ + ARG_AUGMENT_SESSION_AUTH='${var.augment_session_token}' \ + ARG_AUGGIE_MODEL='${var.auggie_model}' \ + ARG_REPORT_TASKS='${var.report_tasks}' \ + /tmp/start.sh + EOT + + install_script = <<-EOT + #!/bin/bash + set -o errexit + set -o pipefail + + echo -n '${base64encode(local.install_script)}' | base64 -d > /tmp/install.sh + chmod +x /tmp/install.sh + ARG_AUGGIE_INSTALL='${var.install_auggie}' \ + ARG_AUGGIE_VERSION='${var.auggie_version}' \ + ARG_MCP_APP_STATUS_SLUG='${local.app_slug}' \ + ARG_AUGGIE_RULES='${base64encode(var.rules)}' \ + ARG_MCP_CONFIG='${var.mcp != null ? base64encode(replace(var.mcp, "'", "'\\''")) : ""}' \ + /tmp/install.sh + EOT +} \ No newline at end of file diff --git a/registry/coder-labs/modules/auggie/scripts/install.sh b/registry/coder-labs/modules/auggie/scripts/install.sh new file mode 100644 index 000000000..e6606e8ef --- /dev/null +++ b/registry/coder-labs/modules/auggie/scripts/install.sh @@ -0,0 +1,127 @@ +#!/bin/bash +set -euo pipefail + +source "$HOME"/.bashrc + +BOLD='\033[0;1m' + +# Function to check if a command exists +command_exists() { + command -v "$1" > /dev/null 2>&1 +} + +ARG_AUGGIE_INSTALL=${ARG_AUGGIE_INSTALL:-true} +ARG_AUGGIE_VERSION=${ARG_AUGGIE_VERSION:-} +ARG_MCP_APP_STATUS_SLUG=${ARG_MCP_APP_STATUS_SLUG:-} +ARG_AUGGIE_RULES=$(echo -n "${ARG_AUGGIE_RULES:-}" | base64 -d) +ARG_MCP_CONFIG=${ARG_MCP_CONFIG:-} + +echo "--------------------------------" + +printf "install auggie: %s\n" "$ARG_AUGGIE_INSTALL" +printf "auggie_version: %s\n" "$ARG_AUGGIE_VERSION" +printf "app_slug: %s\n" "$ARG_MCP_APP_STATUS_SLUG" +printf "rules: %s\n" "$ARG_AUGGIE_RULES" + +echo "--------------------------------" + + +function check_dependencies() { + if ! command_exists node; then + printf "Error: Node.js is not installed. Please install Node.js manually or use the pre_install_script to install it.\n" + exit 1 + fi + + if ! command_exists npm; then + printf "Error: npm is not installed. Please install npm manually or use the pre_install_script to install it.\n" + exit 1 + fi + + printf "Node.js version: %s\n" "$(node --version)" + printf "npm version: %s\n" "$(npm --version)" +} + +function install_auggie() { + if [ "${ARG_AUGGIE_INSTALL}" = "true" ]; then + check_dependencies + + printf "%s Installing Auggie CLI\n" "${BOLD}" + + NPM_GLOBAL_PREFIX="${HOME}/.npm-global" + if [ ! -d "$NPM_GLOBAL_PREFIX" ]; then + mkdir -p "$NPM_GLOBAL_PREFIX" + fi + + npm config set prefix "$NPM_GLOBAL_PREFIX" + + export PATH="$NPM_GLOBAL_PREFIX/bin:$PATH" + + if [ -n "$ARG_AUGGIE_VERSION" ]; then + npm install -g "@augmentcode/auggie@$ARG_AUGGIE_VERSION" + else + npm install -g "@augmentcode/auggie" + fi + + if ! grep -q "export PATH=\"\$HOME/.npm-global/bin:\$PATH\"" "$HOME/.bashrc"; then + echo 'export PATH="$HOME/.npm-global/bin:$PATH"' >> "$HOME/.bashrc" + fi + + printf "%s Successfully installed Auggie CLI. Version: %s\n" "${BOLD}" "$(auggie --version)" + else + printf "Skipping Auggie CLI installation (install_auggie=false)\n" + fi +} + + +function create_coder_mcp() { + AUGGIE_CODER_MCP_FILE="$HOME/.augment/coder_mcp.json" + CODER_MCP=$( + cat << EOF +{ + "mcpServers":{ + "coder": { + "args": ["exp", "mcp", "server"], + "command": "coder", + "env": { + "CODER_MCP_APP_STATUS_SLUG": "${ARG_MCP_APP_STATUS_SLUG}", + "CODER_MCP_AI_AGENTAPI_URL": "http://localhost:3284", + "CODER_AGENT_URL": "${CODER_AGENT_URL:-}", + "CODER_AGENT_TOKEN": "${CODER_AGENT_TOKEN:-}" + } + } + } +} +EOF + ) + mkdir -p "$(dirname "$AUGGIE_CODER_MCP_FILE")" + echo "$CODER_MCP" > "$AUGGIE_CODER_MCP_FILE" + printf "Coder MCP config created at: %s\n" "$AUGGIE_CODER_MCP_FILE" +} + +function create_user_mcp() { + if [ -n "$ARG_MCP_CONFIG" ]; then + USER_MCP_CONFIG_FILE="$HOME/.augment/user_mcp.json" + USER_MCP_CONTENT=$(echo -n "$ARG_MCP_CONFIG" | base64 -d) + mkdir -p "$(dirname "$USER_MCP_CONFIG_FILE")" + echo "$USER_MCP_CONTENT" > "$USER_MCP_CONFIG_FILE" + printf "User MCP config created at: %s\n" "$USER_MCP_CONFIG_FILE" + else + printf "No user MCP config provided, skipping user MCP config creation.\n" + fi +} + +function create_rules_file() { + AUGGIE_RULES_FILE="$HOME/.augment/rules.md" + if [ -n "$ARG_AUGGIE_RULES" ]; then + mkdir -p "$(dirname "$AUGGIE_RULES_FILE")" + echo -n "$ARG_AUGGIE_RULES" > "$AUGGIE_RULES_FILE" + printf "Rules file created at: %s\n" "$AUGGIE_RULES_FILE" + else + printf "No rules provided, skipping rules file creation.\n" + fi +} + +install_auggie +create_coder_mcp +create_user_mcp +create_rules_file diff --git a/registry/coder-labs/modules/auggie/scripts/start.sh b/registry/coder-labs/modules/auggie/scripts/start.sh new file mode 100644 index 000000000..840b808d4 --- /dev/null +++ b/registry/coder-labs/modules/auggie/scripts/start.sh @@ -0,0 +1,104 @@ +#!/bin/bash +set -euo pipefail + +source "$HOME"/.bashrc + +command_exists() { + command -v "$1" > /dev/null 2>&1 +} + +if [ -f "$HOME/.nvm/nvm.sh" ]; then + source "$HOME"/.nvm/nvm.sh +else + export PATH="$HOME/.npm-global/bin:$PATH" +fi + +ARG_AUGGIE_START_DIRECTORY=${ARG_AUGGIE_START_DIRECTORY:-"$HOME"} +ARG_TASK_PROMPT=$(echo -n "${ARG_TASK_PROMPT:-}" | base64 -d) +ARG_MCP_FILES=${ARG_MCP_FILES:-[]} +ARG_AUGGIE_RULES=${ARG_AUGGIE_RULES:-} +ARG_AUGMENT_SESSION_AUTH=${ARG_AUGMENT_SESSION_AUTH:-} +ARG_AUGGIE_CONTINUE_PREVIOUS_CONVERSATION=${ARG_AUGGIE_CONTINUE_PREVIOUS_CONVERSATION:-false} +ARG_AUGGIE_INTERACTION_MODE=${ARG_AUGGIE_INTERACTION_MODE:-"interactive"} +ARG_AUGGIE_MODEL=${ARG_AUGGIE_MODEL:-} +ARG_REPORT_TASKS=${ARG_REPORT_TASKS:-false} + +ARGS=() + +echo "--------------------------------" + +printf "auggie_start_directory: %s\n" "$ARG_AUGGIE_START_DIRECTORY" +printf "task_prompt: %s\n" "$ARG_TASK_PROMPT" +printf "mcp_files: %s\n" "$ARG_MCP_FILES" +printf "auggie_rules: %s\n" "$ARG_AUGGIE_RULES" +printf "continue_previous_conversation: %s\n" "$ARG_AUGGIE_CONTINUE_PREVIOUS_CONVERSATION" +printf "auggie_interaction_mode: %s\n" "$ARG_AUGGIE_INTERACTION_MODE" +printf "augment_session_auth: %s\n" "$ARG_AUGMENT_SESSION_AUTH" +printf "auggie_model: %s\n" "$ARG_AUGGIE_MODEL" +printf "report_tasks: %s\n" "$ARG_REPORT_TASKS" + +echo "--------------------------------" + + +function validate_auggie_installation() { + if command_exists auggie; then + printf "Auggie is installed\n" + else + printf "Error: Auggie is not installed. Please enable install_auggie or install it manually\n" + exit 1 + fi +} + +function build_auggie_args() { + if [ -n "$ARG_AUGGIE_INTERACTION_MODE" ]; then + if [ "$ARG_AUGGIE_INTERACTION_MODE" != "interactive" ]; then + ARGS+=(--"$ARG_AUGGIE_INTERACTION_MODE") + fi + fi + + if [ -n "$ARG_AUGGIE_MODEL" ]; then + ARGS+=(--model "$ARG_AUGGIE_MODEL") + fi + + if [ -f "$HOME/.augment/user_mcp.json" ]; then + ARGS+=(--mcp-config "$HOME/.augment/user_mcp.json") + fi + + if [ -n "$ARG_MCP_FILES" ] && [ "$ARG_MCP_FILES" != "[]" ]; then + for file in $(echo "$ARG_MCP_FILES" | jq -r '.[]'); do + ARGS+=(--mcp-config "$file") + done + fi + + ARGS+=(--mcp-config "$HOME/.augment/coder_mcp.json") + + if [ -n "$ARG_AUGGIE_RULES" ]; then + AUGGIE_RULES_FILE="$HOME/.augment/rules.md" + ARGS+=(--rules "$AUGGIE_RULES_FILE") + fi + + if [ "$ARG_AUGGIE_CONTINUE_PREVIOUS_CONVERSATION" == "true" ]; then + ARGS+=(--continue) + fi + + if [ -n "$ARG_TASK_PROMPT" ]; then + if [ "$ARG_REPORT_TASKS" == "true" ]; then + PROMPT="Every step of the way, report your progress using coder_report_task tool with proper summary and statuses. Your task at hand: $ARG_TASK_PROMPT" + else + PROMPT="$ARG_TASK_PROMPT" + fi + ARGS+=(--instruction "$PROMPT") + fi +} + +function start_agentapi_server() { + mkdir -p "$ARG_AUGGIE_START_DIRECTORY" + cd "$ARG_AUGGIE_START_DIRECTORY" + ARGS+=(--workspace-root "$ARG_AUGGIE_START_DIRECTORY") + printf "Running auggie with args: %s\n" "$(printf '%q ' "${ARGS[@]}")" + agentapi server --term-width 67 --term-height 1190 -- auggie "${ARGS[@]}" +} + +validate_auggie_installation +build_auggie_args +start_agentapi_server diff --git a/registry/coder-labs/modules/auggie/testdata/auggie-mock.sh b/registry/coder-labs/modules/auggie/testdata/auggie-mock.sh new file mode 100644 index 000000000..ba7c5f6db --- /dev/null +++ b/registry/coder-labs/modules/auggie/testdata/auggie-mock.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +if [[ "$1" == "--version" ]]; then + echo "HELLO: $(bash -c env)" + echo "auggie version v1.0.0" + exit 0 +fi + +set -e + +while true; do + echo "$(date) - auggie-mock" + sleep 15 +done