From 1d1be3d56bfc8d861dd47aae559546c19bfdec61 Mon Sep 17 00:00:00 2001 From: DevCats Date: Mon, 29 Sep 2025 16:15:18 -0500 Subject: [PATCH 01/58] feat(copilot-cli): Initial Commit --- .../coder-labs/modules/copilot-cli/README.md | 144 +++++++++ .../copilot-cli/copilot-cli.tftest.hcl | 238 ++++++++++++++ .../modules/copilot-cli/main.test.ts | 290 ++++++++++++++++++ .../coder-labs/modules/copilot-cli/main.tf | 242 +++++++++++++++ .../modules/copilot-cli/scripts/install.sh | 121 ++++++++ .../modules/copilot-cli/scripts/start.sh | 105 +++++++ .../copilot-cli/testdata/copilot-mock.sh | 12 + 7 files changed, 1152 insertions(+) create mode 100644 registry/coder-labs/modules/copilot-cli/README.md create mode 100644 registry/coder-labs/modules/copilot-cli/copilot-cli.tftest.hcl create mode 100644 registry/coder-labs/modules/copilot-cli/main.test.ts create mode 100644 registry/coder-labs/modules/copilot-cli/main.tf create mode 100644 registry/coder-labs/modules/copilot-cli/scripts/install.sh create mode 100644 registry/coder-labs/modules/copilot-cli/scripts/start.sh create mode 100644 registry/coder-labs/modules/copilot-cli/testdata/copilot-mock.sh diff --git a/registry/coder-labs/modules/copilot-cli/README.md b/registry/coder-labs/modules/copilot-cli/README.md new file mode 100644 index 000000000..c1070b0a7 --- /dev/null +++ b/registry/coder-labs/modules/copilot-cli/README.md @@ -0,0 +1,144 @@ +--- +display_name: Copilot CLI +description: GitHub Copilot CLI agent for AI-powered terminal assistance +icon: ../../../../.icons/github.svg +verified: false +tags: [agent, copilot, ai, github, cli, tasks] +--- + +# Copilot CLI + +Run [GitHub Copilot CLI](https://docs.github.com/copilot/concepts/agents/about-copilot-cli) in your workspace for AI-powered coding assistance directly from the terminal. This module integrates with [AgentAPI](https://github.com/coder/agentapi) for task reporting in the Coder UI. + +```tf +module "copilot_cli" { + source = "registry.coder.com/coder-labs/copilot-cli/coder" + version = "1.0.0" + agent_id = coder_agent.example.id + workdir = "/home/coder/project" +} +``` + +> [!NOTE] +> By default, this module is configured to run the embedded chat interface as a path-based application. In production, we recommend that you configure a [wildcard access URL](https://coder.com/docs/admin/setup#wildcard-access-url) and set `subdomain = true`. See [here](https://coder.com/docs/tutorials/best-practices/security-best-practices#disable-path-based-apps) for more details. + +## Prerequisites + +- **Node.js v22+** and **npm v10+** +- **GitHub Copilot CLI**: `npm install -g @github/copilot` +- **Active Copilot subscription** (GitHub Copilot Pro, Pro+, Business, or Enterprise) +- **GitHub external authentication** configured in Coder + +## Examples + +### Usage with Tasks and Advanced Configuration + +```tf +data "coder_parameter" "ai_prompt" { + type = "string" + name = "AI Prompt" + default = "" + description = "Initial task prompt for Copilot CLI." + mutable = true +} + +module "copilot_cli" { + source = "registry.coder.com/coder-labs/copilot-cli/coder" + version = "1.0.0" + agent_id = coder_agent.example.id + workdir = "/home/coder/project" + + ai_prompt = data.coder_parameter.ai_prompt.value + copilot_model = "claude-sonnet-4.5" + + allow_tools = ["shell(git)", "shell(npm)", "write"] + trusted_directories = ["/home/coder/workspace", "/tmp"] + + mcp_config = jsonencode({ + mcpServers = { + github = { + command = "@github/copilot-mcp-github" + } + } + }) + + pre_install_script = <<-EOT + #!/bin/bash + curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - + sudo apt-get install -y nodejs + npm install -g @github/copilot + EOT +} +``` + +### Standalone Mode + +Run and configure Copilot CLI as a standalone tool in your workspace. + +```tf +module "copilot_cli" { + source = "registry.coder.com/coder-labs/copilot-cli/coder" + version = "1.0.0" + agent_id = coder_agent.example.id + workdir = "/home/coder" + report_tasks = false + cli_app = true +} +``` + +### Custom Configuration + +You can customize the entire Copilot CLI configuration: + +```tf +module "copilot_cli" { + source = "registry.coder.com/coder-labs/copilot-cli/coder" + version = "1.0.0" + agent_id = coder_agent.example.id + workdir = "/home/coder/projects" + + copilot_config = jsonencode({ + banner = "auto" + theme = "dark" + trusted_folders = [ + "/home/coder/workspace", + "/home/coder/projects" + ] + }) +} +``` + +## Authentication + +This module uses Coder's GitHub external authentication: +- Users authenticate via GitHub OAuth in the Coder UI +- Copilot CLI automatically uses the authenticated session +- No manual token management required +- If not authenticated, users will be prompted to login via the `/login` slash command + +## Troubleshooting + +If you encounter any issues, check the log files in the `~/.copilot-module` directory within your workspace for detailed information. + +```bash +# Installation logs +cat ~/.copilot-module/install.log + +# Startup logs +cat ~/.copilot-module/agentapi-start.log + +# Pre/post install script logs +cat ~/.copilot-module/pre_install.log +cat ~/.copilot-module/post_install.log +``` + +> [!NOTE] +> To use tasks with Copilot CLI, you must have an active GitHub Copilot subscription. +> The `workdir` variable is required and specifies the directory where Copilot CLI will run. + +## References + +- [GitHub Copilot CLI Documentation](https://docs.github.com/en/copilot/concepts/agents/about-copilot-cli) +- [Installing GitHub Copilot CLI](https://docs.github.com/en/copilot/how-tos/set-up/install-copilot-cli) +- [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/copilot-cli/copilot-cli.tftest.hcl b/registry/coder-labs/modules/copilot-cli/copilot-cli.tftest.hcl new file mode 100644 index 000000000..a28ca4d1c --- /dev/null +++ b/registry/coder-labs/modules/copilot-cli/copilot-cli.tftest.hcl @@ -0,0 +1,238 @@ +run "required_variables" { + command = plan + + variables { + agent_id = "test-agent-id" + workdir = "/home/coder" + external_auth_id = "github" + } + + assert { + condition = var.agent_id == "test-agent-id" + error_message = "Agent ID should be set correctly" + } + + assert { + condition = var.workdir == "/home/coder" + error_message = "Workdir should be set correctly" + } + + assert { + condition = var.external_auth_id == "github" + error_message = "External auth ID should be set correctly" + } +} + +run "minimal_config" { + command = plan + + variables { + agent_id = "test-agent-id" + workdir = "/home/coder" + external_auth_id = "github" + } + + assert { + condition = resource.coder_env.mcp_app_status_slug.name == "CODER_MCP_APP_STATUS_SLUG" + error_message = "Status slug environment variable not configured correctly" + } + + assert { + condition = resource.coder_env.mcp_app_status_slug.value == "copilot-cli" + error_message = "Status slug value should be 'copilot-cli'" + } + + assert { + condition = var.copilot_model == "claude-sonnet-4" + error_message = "Default model should be 'claude-sonnet-4'" + } + + assert { + condition = var.report_tasks == true + error_message = "Task reporting should be enabled by default" + } +} + +run "custom_model" { + command = plan + + variables { + agent_id = "test-agent-id" + workdir = "/home/coder" + external_auth_id = "github" + copilot_model = "claude-sonnet-4.5" + } + + assert { + condition = var.copilot_model == "claude-sonnet-4.5" + error_message = "Custom model should be set correctly" + } +} + +run "custom_copilot_config" { + command = plan + + variables { + agent_id = "test-agent-id" + workdir = "/home/coder" + external_auth_id = "github" + copilot_config = jsonencode({ + banner = "auto" + theme = "light" + trusted_folders = ["/home/coder", "/workspace"] + }) + } + + assert { + condition = var.copilot_config != "" + error_message = "Custom copilot config should be provided" + } +} + +run "trusted_directories" { + command = plan + + variables { + agent_id = "test-agent-id" + workdir = "/home/coder" + external_auth_id = "github" + trusted_directories = ["/workspace", "/projects"] + } + + assert { + condition = length(var.trusted_directories) == 2 + error_message = "Trusted directories should be set correctly" + } +} + +run "mcp_config" { + command = plan + + variables { + agent_id = "test-agent-id" + workdir = "/home/coder" + external_auth_id = "github" + mcp_config = jsonencode({ + mcpServers = { + custom = { + command = "custom-server" + args = ["--config", "custom.json"] + } + } + }) + } + + assert { + condition = var.mcp_config != "" + error_message = "Custom MCP config should be provided" + } +} + +run "tool_permissions" { + command = plan + + variables { + agent_id = "test-agent-id" + workdir = "/home/coder" + external_auth_id = "github" + allow_tools = ["fs_read", "fs_write"] + deny_tools = ["execute_bash"] + } + + assert { + condition = length(var.allow_tools) == 2 + error_message = "Allow tools should be set correctly" + } + + assert { + condition = length(var.deny_tools) == 1 + error_message = "Deny tools should be set correctly" + } +} + +run "ui_customization" { + command = plan + + variables { + agent_id = "test-agent-id" + workdir = "/home/coder" + external_auth_id = "github" + order = 5 + group = "AI Tools" + icon = "/icon/custom-copilot.svg" + } + + assert { + condition = var.order == 5 + error_message = "Order should be set correctly" + } + + assert { + condition = var.group == "AI Tools" + error_message = "Group should be set correctly" + } + + assert { + condition = var.icon == "/icon/custom-copilot.svg" + error_message = "Icon should be set correctly" + } +} + +run "install_scripts" { + command = plan + + variables { + agent_id = "test-agent-id" + workdir = "/home/coder" + external_auth_id = "github" + pre_install_script = "echo 'Pre-install setup'" + post_install_script = "echo 'Post-install cleanup'" + } + + assert { + condition = var.pre_install_script == "echo 'Pre-install setup'" + error_message = "Pre-install script should be set correctly" + } + + assert { + condition = var.post_install_script == "echo 'Post-install cleanup'" + error_message = "Post-install script should be set correctly" + } +} + +run "model_validation" { + command = plan + + variables { + agent_id = "test-agent-id" + workdir = "/home/coder" + external_auth_id = "github" + copilot_model = "gpt-5" + } + + assert { + condition = contains(["claude-sonnet-4", "claude-sonnet-4.5", "gpt-5"], var.copilot_model) + error_message = "Model should be one of the valid options" + } +} + +run "task_reporting_disabled" { + command = plan + + variables { + agent_id = "test-agent-id" + workdir = "/home/coder" + external_auth_id = "github" + report_tasks = false + } + + assert { + condition = var.report_tasks == false + error_message = "Task reporting should be disabled when set to false" + } + + assert { + condition = resource.coder_env.mcp_app_status_slug.name == "CODER_MCP_APP_STATUS_SLUG" + error_message = "Status slug should still be configured even when task reporting is disabled" + } +} \ No newline at end of file diff --git a/registry/coder-labs/modules/copilot-cli/main.test.ts b/registry/coder-labs/modules/copilot-cli/main.test.ts new file mode 100644 index 000000000..fdd502ea2 --- /dev/null +++ b/registry/coder-labs/modules/copilot-cli/main.test.ts @@ -0,0 +1,290 @@ +import { describe, it, expect } from "bun:test"; +import { + runTerraformApply, + runTerraformInit, + findResourceInstance, +} from "~test"; +import path from "path"; + +const moduleDir = path.resolve(__dirname); + +const requiredVars = { + agent_id: "test-agent-id", + workdir: "/home/coder", + external_auth_id: "github", +}; + +const fullConfigVars = { + agent_id: "test-agent-id", + workdir: "/home/coder", + external_auth_id: "github", + copilot_model: "claude-sonnet-4.5", + report_tasks: true, + order: 1, + group: "AI Tools", + icon: "/icon/custom-copilot.svg", + pre_install_script: "echo 'Starting pre-install'", + post_install_script: "echo 'Completed post-install'", + copilot_config: JSON.stringify({ + banner: "auto", + theme: "light", + trusted_folders: ["/home/coder", "/workspace"], + }), + mcp_config: JSON.stringify({ + mcpServers: { + github: { + command: "@github/copilot-mcp-github", + env: { + GITHUB_TOKEN: "${GITHUB_TOKEN}", + }, + }, + custom: { + command: "custom-server", + args: ["--config", "custom.json"], + }, + }, + }), + trusted_directories: ["/workspace", "/projects"], + allow_tools: ["fs_read", "fs_write"], + deny_tools: ["execute_bash"], +}; + +describe("copilot-cli module", async () => { + await runTerraformInit(moduleDir); + + it("works with required variables", async () => { + const state = await runTerraformApply(moduleDir, requiredVars); + + const statusSlugEnv = findResourceInstance( + state, + "coder_env", + "mcp_app_status_slug", + ); + expect(statusSlugEnv).toBeDefined(); + expect(statusSlugEnv.name).toBe("CODER_MCP_APP_STATUS_SLUG"); + expect(statusSlugEnv.value).toBe("copilot-cli"); + }); + + it("creates required environment variables", async () => { + const state = await runTerraformApply(moduleDir, fullConfigVars); + + const statusSlugEnv = findResourceInstance( + state, + "coder_env", + "mcp_app_status_slug", + ); + expect(statusSlugEnv).toBeDefined(); + expect(statusSlugEnv.name).toBe("CODER_MCP_APP_STATUS_SLUG"); + expect(statusSlugEnv.value).toBe("copilot-cli"); + }); + + it("uses default model when not specified", async () => { + const state = await runTerraformApply(moduleDir, requiredVars); + + const statusSlugEnv = findResourceInstance( + state, + "coder_env", + "mcp_app_status_slug", + ); + expect(statusSlugEnv).toBeDefined(); + }); + + it("supports custom copilot model", async () => { + const customModelVars = { + ...requiredVars, + copilot_model: "claude-sonnet-4.5", + }; + + const state = await runTerraformApply(moduleDir, customModelVars); + + const statusSlugEnv = findResourceInstance( + state, + "coder_env", + "mcp_app_status_slug", + ); + expect(statusSlugEnv).toBeDefined(); + }); + + it("supports custom copilot configuration", async () => { + const customConfigVars = { + ...requiredVars, + copilot_config: JSON.stringify({ + banner: "auto", + theme: "dark", + trusted_folders: ["/home/coder", "/workspace"], + }), + }; + + const state = await runTerraformApply(moduleDir, customConfigVars); + + const statusSlugEnv = findResourceInstance( + state, + "coder_env", + "mcp_app_status_slug", + ); + expect(statusSlugEnv).toBeDefined(); + }); + + it("supports trusted directories", async () => { + const trustedDirsVars = { + ...requiredVars, + trusted_directories: ["/workspace", "/projects", "/data"], + }; + + const state = await runTerraformApply(moduleDir, trustedDirsVars); + + const statusSlugEnv = findResourceInstance( + state, + "coder_env", + "mcp_app_status_slug", + ); + expect(statusSlugEnv).toBeDefined(); + }); + + it("supports custom MCP configuration", async () => { + const mcpConfigVars = { + ...requiredVars, + mcp_config: JSON.stringify({ + mcpServers: { + filesystem: { + command: "npx", + args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"], + }, + github: { + command: "@github/copilot-mcp-github", + env: { + GITHUB_TOKEN: "${GITHUB_TOKEN}", + }, + }, + }, + }), + }; + + const state = await runTerraformApply(moduleDir, mcpConfigVars); + + const statusSlugEnv = findResourceInstance( + state, + "coder_env", + "mcp_app_status_slug", + ); + expect(statusSlugEnv).toBeDefined(); + }); + + it("supports tool permissions", async () => { + const toolPermissionsVars = { + ...requiredVars, + allow_tools: ["fs_read", "fs_write", "execute_bash"], + deny_tools: ["rm", "sudo"], + }; + + const state = await runTerraformApply(moduleDir, toolPermissionsVars); + + const statusSlugEnv = findResourceInstance( + state, + "coder_env", + "mcp_app_status_slug", + ); + expect(statusSlugEnv).toBeDefined(); + }); + + it("supports UI customization options", async () => { + const uiCustomVars = { + ...requiredVars, + order: 5, + group: "Custom AI Tools", + icon: "/icon/custom-copilot-icon.svg", + }; + + const state = await runTerraformApply(moduleDir, uiCustomVars); + + const statusSlugEnv = findResourceInstance( + state, + "coder_env", + "mcp_app_status_slug", + ); + expect(statusSlugEnv).toBeDefined(); + }); + + it("supports pre and post install scripts", async () => { + const scriptVars = { + ...requiredVars, + pre_install_script: "echo 'Pre-install setup for Copilot CLI'", + post_install_script: "echo 'Post-install cleanup for Copilot CLI'", + }; + + const state = await runTerraformApply(moduleDir, scriptVars); + + const statusSlugEnv = findResourceInstance( + state, + "coder_env", + "mcp_app_status_slug", + ); + expect(statusSlugEnv).toBeDefined(); + }); + + it("handles task reporting disabled", async () => { + const noReportingVars = { + ...requiredVars, + report_tasks: false, + }; + + const state = await runTerraformApply(moduleDir, noReportingVars); + + const statusSlugEnv = findResourceInstance( + state, + "coder_env", + "mcp_app_status_slug", + ); + expect(statusSlugEnv).toBeDefined(); + expect(statusSlugEnv.value).toBe("copilot-cli"); + }); + + it("validates model options", async () => { + const validModels = ["claude-sonnet-4", "claude-sonnet-4.5", "gpt-5"]; + + for (const model of validModels) { + const modelVars = { + ...requiredVars, + copilot_model: model, + }; + + const state = await runTerraformApply(moduleDir, modelVars); + + const statusSlugEnv = findResourceInstance( + state, + "coder_env", + "mcp_app_status_slug", + ); + expect(statusSlugEnv).toBeDefined(); + } + }); + + it("supports external auth configuration", async () => { + const customAuthVars = { + ...requiredVars, + external_auth_id: "custom-github", + }; + + const state = await runTerraformApply(moduleDir, customAuthVars); + + const statusSlugEnv = findResourceInstance( + state, + "coder_env", + "mcp_app_status_slug", + ); + expect(statusSlugEnv).toBeDefined(); + }); + + it("works with full configuration", async () => { + const state = await runTerraformApply(moduleDir, fullConfigVars); + + const statusSlugEnv = findResourceInstance( + state, + "coder_env", + "mcp_app_status_slug", + ); + expect(statusSlugEnv).toBeDefined(); + expect(statusSlugEnv.name).toBe("CODER_MCP_APP_STATUS_SLUG"); + expect(statusSlugEnv.value).toBe("copilot-cli"); + }); +}); diff --git a/registry/coder-labs/modules/copilot-cli/main.tf b/registry/coder-labs/modules/copilot-cli/main.tf new file mode 100644 index 000000000..5d41c0ff7 --- /dev/null +++ b/registry/coder-labs/modules/copilot-cli/main.tf @@ -0,0 +1,242 @@ +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." +} + +variable "workdir" { + type = string + description = "The folder to run Copilot CLI in." +} + +variable "external_auth_id" { + type = string + description = "ID of the GitHub external auth provider configured in Coder." + default = "github" +} + +variable "copilot_model" { + type = string + description = "Model to use. Supported values: claude-sonnet-4 (default), claude-sonnet-4.5, gpt-5." + default = "claude-sonnet-4" + validation { + condition = contains(["claude-sonnet-4", "claude-sonnet-4.5", "gpt-5"], var.copilot_model) + error_message = "copilot_model must be one of: claude-sonnet-4, claude-sonnet-4.5, gpt-5." + } +} + +variable "copilot_config" { + type = string + description = "Custom Copilot CLI configuration as JSON string. Leave empty to use default configuration with banner disabled, theme set to auto, and workdir as trusted folder." + default = "" +} + +variable "ai_prompt" { + type = string + description = "Initial task prompt for programmatic mode." + default = "" +} + +variable "trusted_directories" { + type = list(string) + description = "Additional directories to trust for Copilot CLI operations." + default = [] +} + +variable "allow_all_tools" { + type = bool + description = "Allow all tools without prompting (equivalent to --allow-all-tools)." + default = false +} + +variable "allow_tools" { + type = list(string) + description = "Specific tools to allow: shell(command), write, or MCP_SERVER_NAME." + default = [] +} + +variable "deny_tools" { + type = list(string) + description = "Specific tools to deny: shell(command), write, or MCP_SERVER_NAME." + default = [] +} + +variable "mcp_config" { + type = string + description = "Custom MCP server configuration as JSON string." + default = "" +} + +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.7.1" +} + +variable "report_tasks" { + type = bool + description = "Whether to enable task reporting to Coder UI via AgentAPI." + default = true +} + +variable "subdomain" { + type = bool + description = "Whether to use a subdomain for AgentAPI." + default = false +} + +variable "order" { + type = number + description = "The order determines the position of app in the UI presentation." + 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/github.svg" +} + +variable "web_app_display_name" { + type = string + description = "Display name for the web app." + default = "Copilot CLI" +} + +variable "cli_app" { + type = bool + description = "Whether to create a CLI app for Copilot CLI." + default = false +} + +variable "cli_app_display_name" { + type = string + description = "Display name for the CLI app." + default = "Copilot CLI" +} + +variable "pre_install_script" { + type = string + description = "Custom script to run before configuring Copilot CLI." + default = null +} + +variable "post_install_script" { + type = string + description = "Custom script to run after configuring Copilot CLI." + default = null +} + +data "coder_workspace" "me" {} +data "coder_workspace_owner" "me" {} + +data "coder_external_auth" "github" { + id = var.external_auth_id +} + +locals { + workdir = trimsuffix(var.workdir, "/") + app_slug = "copilot-cli" + install_script = file("${path.module}/scripts/install.sh") + start_script = file("${path.module}/scripts/start.sh") + module_dir_name = ".copilot-module" + + # Default configuration with workdir as trusted folder + default_copilot_config = jsonencode({ + banner = "never" + theme = "auto" + trusted_folders = concat([local.workdir], var.trusted_directories) + }) + + final_copilot_config = var.copilot_config != "" ? var.copilot_config : local.default_copilot_config +} + +resource "coder_env" "mcp_app_status_slug" { + agent_id = var.agent_id + name = "CODER_MCP_APP_STATUS_SLUG" + value = local.app_slug +} + +resource "coder_env" "copilot_model" { + count = var.copilot_model != "claude-sonnet-4" ? 1 : 0 + agent_id = var.agent_id + name = "COPILOT_MODEL" + value = var.copilot_model +} + + +module "agentapi" { + source = "registry.coder.com/coder/agentapi/coder" + version = "1.1.1" + + agent_id = var.agent_id + folder = local.workdir + 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='${local.workdir}' \ + ARG_AI_PROMPT='${base64encode(var.ai_prompt)}' \ + ARG_COPILOT_MODEL='${var.copilot_model}' \ + ARG_ALLOW_ALL_TOOLS='${var.allow_all_tools}' \ + ARG_ALLOW_TOOLS='${join(",", var.allow_tools)}' \ + ARG_DENY_TOOLS='${join(",", var.deny_tools)}' \ + ARG_TRUSTED_DIRECTORIES='${join(",", var.trusted_directories)}' \ + /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_MCP_APP_STATUS_SLUG='${local.app_slug}' \ + ARG_REPORT_TASKS='${var.report_tasks}' \ + ARG_WORKDIR='${local.workdir}' \ + ARG_MCP_CONFIG='${var.mcp_config != "" ? base64encode(var.mcp_config) : ""}' \ + ARG_COPILOT_CONFIG='${base64encode(local.final_copilot_config)}' \ + ARG_EXTERNAL_AUTH_ID='${var.external_auth_id}' \ + /tmp/install.sh + EOT +} \ No newline at end of file diff --git a/registry/coder-labs/modules/copilot-cli/scripts/install.sh b/registry/coder-labs/modules/copilot-cli/scripts/install.sh new file mode 100644 index 000000000..2726be774 --- /dev/null +++ b/registry/coder-labs/modules/copilot-cli/scripts/install.sh @@ -0,0 +1,121 @@ +#!/bin/bash +set -euo pipefail + +source "$HOME"/.bashrc + +command_exists() { + command -v "$1" > /dev/null 2>&1 +} + +ARG_WORKDIR=${ARG_WORKDIR:-"$HOME"} +ARG_REPORT_TASKS=${ARG_REPORT_TASKS:-true} +ARG_MCP_APP_STATUS_SLUG=${ARG_MCP_APP_STATUS_SLUG:-} +ARG_MCP_CONFIG=$(echo -n "${ARG_MCP_CONFIG:-}" | base64 -d 2>/dev/null || echo "") +ARG_COPILOT_CONFIG=$(echo -n "${ARG_COPILOT_CONFIG:-}" | base64 -d 2>/dev/null || echo "") +ARG_EXTERNAL_AUTH_ID=${ARG_EXTERNAL_AUTH_ID:-github} + +validate_prerequisites() { + if ! command_exists node; then + echo "ERROR: Node.js not found. Copilot CLI requires Node.js v22+." + echo "Install with: curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - && sudo apt-get install -y nodejs" + exit 1 + fi + + if ! command_exists npm; then + echo "ERROR: npm not found. Copilot CLI requires npm v10+." + exit 1 + fi + + if ! command_exists copilot; then + echo "ERROR: Copilot CLI not installed. Run: npm install -g @github/copilot" + exit 1 + fi + + node_version=$(node --version | sed 's/v//' | cut -d. -f1) + if [ "$node_version" -lt 22 ]; then + echo "WARNING: Node.js v$node_version detected. Copilot CLI requires v22+." + fi +} + +check_github_authentication() { + echo "Checking GitHub authentication via Coder external auth..." + + if command_exists coder; then + if coder external-auth access-token "${ARG_EXTERNAL_AUTH_ID:-github}" >/dev/null 2>&1; then + echo "GitHub authentication via Coder external auth: OK" + return 0 + else + echo "WARNING: GitHub external auth not configured or expired" + echo "Please authenticate with GitHub in the Coder UI" + fi + fi + + if command_exists gh && gh auth status >/dev/null 2>&1; then + echo "GitHub CLI authentication detected as fallback" + return 0 + fi + + echo "WARNING: No GitHub authentication found. Copilot CLI requires:" + echo " - GitHub external authentication configured in Coder (recommended)" + echo " - Or GitHub CLI with 'gh auth login'" +} + +setup_copilot_configurations() { + mkdir -p "$ARG_WORKDIR" + + local module_path="$HOME/.copilot-module" + mkdir -p "$module_path" + mkdir -p "$HOME/.config" + + if [ -n "$ARG_MCP_CONFIG" ]; then + echo "Configuring custom MCP servers..." + echo "$ARG_MCP_CONFIG" > "$module_path/mcp_config.json" + else + cat > "$module_path/mcp_config.json" << 'EOF' +{ + "mcpServers": { + "github": { + "command": "@github/copilot-mcp-github" + } + } +} +EOF + fi + + setup_copilot_config + + echo "$ARG_WORKDIR" > "$module_path/trusted_directories" +} + +setup_copilot_config() { + local config_file="$HOME/.config/copilot.json" + + if [ -n "$ARG_COPILOT_CONFIG" ]; then + echo "Setting up Copilot configuration..." + echo "$ARG_COPILOT_CONFIG" > "$config_file" + else + echo "ERROR: No Copilot configuration provided" + exit 1 + fi +} + +configure_coder_integration() { + if [ "$ARG_REPORT_TASKS" = "true" ]; then + echo "Configuring Copilot CLI task reporting..." + export CODER_MCP_APP_STATUS_SLUG="$ARG_MCP_APP_STATUS_SLUG" + export CODER_MCP_AI_AGENTAPI_URL="http://localhost:3284" + + if command_exists coder; then + coder exp mcp configure copilot-cli "$ARG_WORKDIR" 2>/dev/null || true + fi + else + echo "Task reporting disabled." + fi +} + +validate_prerequisites +check_github_authentication +setup_copilot_configurations +configure_coder_integration + +echo "Copilot CLI module setup completed." diff --git a/registry/coder-labs/modules/copilot-cli/scripts/start.sh b/registry/coder-labs/modules/copilot-cli/scripts/start.sh new file mode 100644 index 000000000..73ab8b920 --- /dev/null +++ b/registry/coder-labs/modules/copilot-cli/scripts/start.sh @@ -0,0 +1,105 @@ +#!/bin/bash +set -euo pipefail + +source "$HOME"/.bashrc +export PATH="$HOME/.local/bin:$PATH" + +command_exists() { + command -v "$1" >/dev/null 2>&1 +} + +ARG_WORKDIR=${ARG_WORKDIR:-"$HOME"} +ARG_AI_PROMPT=$(echo -n "${ARG_AI_PROMPT:-}" | base64 -d 2>/dev/null || echo "") +ARG_COPILOT_MODEL=${ARG_COPILOT_MODEL:-} +ARG_ALLOW_ALL_TOOLS=${ARG_ALLOW_ALL_TOOLS:-false} +ARG_ALLOW_TOOLS=${ARG_ALLOW_TOOLS:-} +ARG_DENY_TOOLS=${ARG_DENY_TOOLS:-} +ARG_TRUSTED_DIRECTORIES=${ARG_TRUSTED_DIRECTORIES:-} + +validate_copilot_installation() { + if ! command_exists copilot; then + echo "ERROR: Copilot CLI not installed. Run: npm install -g @github/copilot" + exit 1 + fi +} + + +build_copilot_args() { + local args=() + + if [ -n "$ARG_AI_PROMPT" ]; then + args+=(--prompt "$ARG_AI_PROMPT") + fi + + if [ "$ARG_ALLOW_ALL_TOOLS" = "true" ]; then + args+=(--allow-all-tools) + fi + + if [ -n "$ARG_ALLOW_TOOLS" ]; then + IFS=',' read -ra ALLOW_ARRAY <<< "$ARG_ALLOW_TOOLS" + for tool in "${ALLOW_ARRAY[@]}"; do + if [ -n "$tool" ]; then + args+=(--allow-tool "$tool") + fi + done + fi + + if [ -n "$ARG_DENY_TOOLS" ]; then + IFS=',' read -ra DENY_ARRAY <<< "$ARG_DENY_TOOLS" + for tool in "${DENY_ARRAY[@]}"; do + if [ -n "$tool" ]; then + args+=(--deny-tool "$tool") + fi + done + fi + + echo "${args[@]}" +} + +configure_copilot_model() { + if [ -n "$ARG_COPILOT_MODEL" ]; then + case "$ARG_COPILOT_MODEL" in + "gpt-5") + export COPILOT_MODEL="gpt-5" + ;; + "claude-sonnet-4") + export COPILOT_MODEL="claude-sonnet-4" + ;; + "claude-sonnet-4.5") + export COPILOT_MODEL="claude-sonnet-4.5" + ;; + *) + echo "WARNING: Unknown model '$ARG_COPILOT_MODEL'. Using default." + ;; + esac + fi +} + +start_agentapi() { + echo "Starting in directory: $ARG_WORKDIR" + cd "$ARG_WORKDIR" + + local copilot_args_str + copilot_args_str=$(build_copilot_args) + + local copilot_args=() + if [ -n "$copilot_args_str" ]; then + read -ra copilot_args <<< "$copilot_args_str" + fi + + if [ ${#copilot_args[@]} -gt 0 ]; then + echo "Copilot arguments: ${copilot_args[*]}" + agentapi server --type claude --term-width 120 --term-height 40 -- copilot "${copilot_args[@]}" + else + echo "Starting Copilot CLI in interactive mode" + agentapi server --type claude --term-width 120 --term-height 40 -- copilot + fi +} + +configure_copilot_model + +echo "COPILOT_MODEL=${ARG_COPILOT_MODEL:-${COPILOT_MODEL:-not set}}" +echo "GitHub authentication: via Coder external auth" + +validate_copilot_installation +start_agentapi diff --git a/registry/coder-labs/modules/copilot-cli/testdata/copilot-mock.sh b/registry/coder-labs/modules/copilot-cli/testdata/copilot-mock.sh new file mode 100644 index 000000000..cd7f6d24b --- /dev/null +++ b/registry/coder-labs/modules/copilot-cli/testdata/copilot-mock.sh @@ -0,0 +1,12 @@ +#!/bin/bash +set -euo pipefail + +if [[ "$1" == "--version" ]]; then + echo "GitHub Copilot CLI v1.0.0" + exit 0 +fi + +while true; do + echo "$(date) - Copilot CLI mock running..." + sleep 15 +done From de1064e8c13f8d2f63696278c674ed8f90f05756 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Mon, 29 Sep 2025 17:06:06 -0500 Subject: [PATCH 02/58] test(copilot-cli): fix test variable formatting and remove model validation test --- .../modules/copilot-cli/main.test.ts | 32 ++++--------------- 1 file changed, 6 insertions(+), 26 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/main.test.ts b/registry/coder-labs/modules/copilot-cli/main.test.ts index fdd502ea2..c37a6b988 100644 --- a/registry/coder-labs/modules/copilot-cli/main.test.ts +++ b/registry/coder-labs/modules/copilot-cli/main.test.ts @@ -44,9 +44,9 @@ const fullConfigVars = { }, }, }), - trusted_directories: ["/workspace", "/projects"], - allow_tools: ["fs_read", "fs_write"], - deny_tools: ["execute_bash"], + trusted_directories: '["/workspace", "/projects"]', + allow_tools: '["fs_read", "fs_write"]', + deny_tools: '["execute_bash"]', }; describe("copilot-cli module", async () => { @@ -128,7 +128,7 @@ describe("copilot-cli module", async () => { it("supports trusted directories", async () => { const trustedDirsVars = { ...requiredVars, - trusted_directories: ["/workspace", "/projects", "/data"], + trusted_directories: '["/workspace", "/projects", "/data"]', }; const state = await runTerraformApply(moduleDir, trustedDirsVars); @@ -173,8 +173,8 @@ describe("copilot-cli module", async () => { it("supports tool permissions", async () => { const toolPermissionsVars = { ...requiredVars, - allow_tools: ["fs_read", "fs_write", "execute_bash"], - deny_tools: ["rm", "sudo"], + allow_tools: '["fs_read", "fs_write", "execute_bash"]', + deny_tools: '["rm", "sudo"]', }; const state = await runTerraformApply(moduleDir, toolPermissionsVars); @@ -239,26 +239,6 @@ describe("copilot-cli module", async () => { expect(statusSlugEnv.value).toBe("copilot-cli"); }); - it("validates model options", async () => { - const validModels = ["claude-sonnet-4", "claude-sonnet-4.5", "gpt-5"]; - - for (const model of validModels) { - const modelVars = { - ...requiredVars, - copilot_model: model, - }; - - const state = await runTerraformApply(moduleDir, modelVars); - - const statusSlugEnv = findResourceInstance( - state, - "coder_env", - "mcp_app_status_slug", - ); - expect(statusSlugEnv).toBeDefined(); - } - }); - it("supports external auth configuration", async () => { const customAuthVars = { ...requiredVars, From 42f5e6af9b8037565ecfbdd7d74b841d32ea08f8 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Mon, 29 Sep 2025 17:06:17 -0500 Subject: [PATCH 03/58] chore: bun run fmt --- registry/coder-labs/modules/copilot-cli/README.md | 1 + .../modules/copilot-cli/copilot-cli.tftest.hcl | 4 ++-- registry/coder-labs/modules/copilot-cli/main.tf | 8 ++++---- .../modules/copilot-cli/scripts/install.sh | 14 +++++++------- .../modules/copilot-cli/scripts/start.sh | 9 ++++----- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/README.md b/registry/coder-labs/modules/copilot-cli/README.md index c1070b0a7..ec0c7436c 100644 --- a/registry/coder-labs/modules/copilot-cli/README.md +++ b/registry/coder-labs/modules/copilot-cli/README.md @@ -111,6 +111,7 @@ module "copilot_cli" { ## Authentication This module uses Coder's GitHub external authentication: + - Users authenticate via GitHub OAuth in the Coder UI - Copilot CLI automatically uses the authenticated session - No manual token management required diff --git a/registry/coder-labs/modules/copilot-cli/copilot-cli.tftest.hcl b/registry/coder-labs/modules/copilot-cli/copilot-cli.tftest.hcl index a28ca4d1c..fef1cd002 100644 --- a/registry/coder-labs/modules/copilot-cli/copilot-cli.tftest.hcl +++ b/registry/coder-labs/modules/copilot-cli/copilot-cli.tftest.hcl @@ -77,8 +77,8 @@ run "custom_copilot_config" { workdir = "/home/coder" external_auth_id = "github" copilot_config = jsonencode({ - banner = "auto" - theme = "light" + banner = "auto" + theme = "light" trusted_folders = ["/home/coder", "/workspace"] }) } diff --git a/registry/coder-labs/modules/copilot-cli/main.tf b/registry/coder-labs/modules/copilot-cli/main.tf index 5d41c0ff7..fdcf392fc 100644 --- a/registry/coder-labs/modules/copilot-cli/main.tf +++ b/registry/coder-labs/modules/copilot-cli/main.tf @@ -161,14 +161,14 @@ locals { install_script = file("${path.module}/scripts/install.sh") start_script = file("${path.module}/scripts/start.sh") module_dir_name = ".copilot-module" - + # Default configuration with workdir as trusted folder default_copilot_config = jsonencode({ - banner = "never" - theme = "auto" + banner = "never" + theme = "auto" trusted_folders = concat([local.workdir], var.trusted_directories) }) - + final_copilot_config = var.copilot_config != "" ? var.copilot_config : local.default_copilot_config } diff --git a/registry/coder-labs/modules/copilot-cli/scripts/install.sh b/registry/coder-labs/modules/copilot-cli/scripts/install.sh index 2726be774..f8e43caeb 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/install.sh +++ b/registry/coder-labs/modules/copilot-cli/scripts/install.sh @@ -10,8 +10,8 @@ command_exists() { ARG_WORKDIR=${ARG_WORKDIR:-"$HOME"} ARG_REPORT_TASKS=${ARG_REPORT_TASKS:-true} ARG_MCP_APP_STATUS_SLUG=${ARG_MCP_APP_STATUS_SLUG:-} -ARG_MCP_CONFIG=$(echo -n "${ARG_MCP_CONFIG:-}" | base64 -d 2>/dev/null || echo "") -ARG_COPILOT_CONFIG=$(echo -n "${ARG_COPILOT_CONFIG:-}" | base64 -d 2>/dev/null || echo "") +ARG_MCP_CONFIG=$(echo -n "${ARG_MCP_CONFIG:-}" | base64 -d 2> /dev/null || echo "") +ARG_COPILOT_CONFIG=$(echo -n "${ARG_COPILOT_CONFIG:-}" | base64 -d 2> /dev/null || echo "") ARG_EXTERNAL_AUTH_ID=${ARG_EXTERNAL_AUTH_ID:-github} validate_prerequisites() { @@ -39,9 +39,9 @@ validate_prerequisites() { check_github_authentication() { echo "Checking GitHub authentication via Coder external auth..." - + if command_exists coder; then - if coder external-auth access-token "${ARG_EXTERNAL_AUTH_ID:-github}" >/dev/null 2>&1; then + if coder external-auth access-token "${ARG_EXTERNAL_AUTH_ID:-github}" > /dev/null 2>&1; then echo "GitHub authentication via Coder external auth: OK" return 0 else @@ -50,7 +50,7 @@ check_github_authentication() { fi fi - if command_exists gh && gh auth status >/dev/null 2>&1; then + if command_exists gh && gh auth status > /dev/null 2>&1; then echo "GitHub CLI authentication detected as fallback" return 0 fi @@ -89,7 +89,7 @@ EOF setup_copilot_config() { local config_file="$HOME/.config/copilot.json" - + if [ -n "$ARG_COPILOT_CONFIG" ]; then echo "Setting up Copilot configuration..." echo "$ARG_COPILOT_CONFIG" > "$config_file" @@ -106,7 +106,7 @@ configure_coder_integration() { export CODER_MCP_AI_AGENTAPI_URL="http://localhost:3284" if command_exists coder; then - coder exp mcp configure copilot-cli "$ARG_WORKDIR" 2>/dev/null || true + coder exp mcp configure copilot-cli "$ARG_WORKDIR" 2> /dev/null || true fi else echo "Task reporting disabled." diff --git a/registry/coder-labs/modules/copilot-cli/scripts/start.sh b/registry/coder-labs/modules/copilot-cli/scripts/start.sh index 73ab8b920..9651c550b 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/start.sh +++ b/registry/coder-labs/modules/copilot-cli/scripts/start.sh @@ -5,11 +5,11 @@ source "$HOME"/.bashrc export PATH="$HOME/.local/bin:$PATH" command_exists() { - command -v "$1" >/dev/null 2>&1 + command -v "$1" > /dev/null 2>&1 } ARG_WORKDIR=${ARG_WORKDIR:-"$HOME"} -ARG_AI_PROMPT=$(echo -n "${ARG_AI_PROMPT:-}" | base64 -d 2>/dev/null || echo "") +ARG_AI_PROMPT=$(echo -n "${ARG_AI_PROMPT:-}" | base64 -d 2> /dev/null || echo "") ARG_COPILOT_MODEL=${ARG_COPILOT_MODEL:-} ARG_ALLOW_ALL_TOOLS=${ARG_ALLOW_ALL_TOOLS:-false} ARG_ALLOW_TOOLS=${ARG_ALLOW_TOOLS:-} @@ -23,7 +23,6 @@ validate_copilot_installation() { fi } - build_copilot_args() { local args=() @@ -78,10 +77,10 @@ configure_copilot_model() { start_agentapi() { echo "Starting in directory: $ARG_WORKDIR" cd "$ARG_WORKDIR" - + local copilot_args_str copilot_args_str=$(build_copilot_args) - + local copilot_args=() if [ -n "$copilot_args_str" ]; then read -ra copilot_args <<< "$copilot_args_str" From f7c4af520906a3923990e60440044556ad8c21e3 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Tue, 30 Sep 2025 16:45:02 -0500 Subject: [PATCH 04/58] feat: add system prompt configuration support --- .../coder-labs/modules/copilot-cli/README.md | 32 +++++++++++++++++++ .../modules/copilot-cli/main.test.ts | 20 ++++++++++++ .../coder-labs/modules/copilot-cli/main.tf | 8 +++++ .../modules/copilot-cli/scripts/start.sh | 12 ++++++- 4 files changed, 71 insertions(+), 1 deletion(-) diff --git a/registry/coder-labs/modules/copilot-cli/README.md b/registry/coder-labs/modules/copilot-cli/README.md index ec0c7436c..fcbf361cf 100644 --- a/registry/coder-labs/modules/copilot-cli/README.md +++ b/registry/coder-labs/modules/copilot-cli/README.md @@ -51,6 +51,13 @@ module "copilot_cli" { ai_prompt = data.coder_parameter.ai_prompt.value copilot_model = "claude-sonnet-4.5" + system_prompt = <<-EOT + You are a helpful AI coding assistant working in a development environment. + Always follow best practices and provide clear explanations for your suggestions. + Focus on writing clean, maintainable code and helping with debugging tasks. + Send a task status update to notify the user that you are ready for input, and then wait for user input. + EOT + allow_tools = ["shell(git)", "shell(npm)", "write"] trusted_directories = ["/home/coder/workspace", "/tmp"] @@ -108,6 +115,31 @@ module "copilot_cli" { } ``` +### System Prompt Configuration + +You can customize the behavior of Copilot CLI by providing a system prompt that will be combined with task prompts: + +```tf +module "copilot_cli" { + source = "registry.coder.com/coder-labs/copilot-cli/coder" + version = "1.0.0" + agent_id = coder_agent.example.id + workdir = "/home/coder/project" + + system_prompt = <<-EOT + You are a senior software engineer helping with code development. + Always prioritize: + - Code quality and best practices + - Security considerations + - Performance optimization + - Clear documentation and comments + + When suggesting changes, explain the reasoning behind your recommendations. + Send a task status update to notify the user that you are ready for input, and then wait for user input. + EOT +} +``` + ## Authentication This module uses Coder's GitHub external authentication: diff --git a/registry/coder-labs/modules/copilot-cli/main.test.ts b/registry/coder-labs/modules/copilot-cli/main.test.ts index c37a6b988..3b29907bc 100644 --- a/registry/coder-labs/modules/copilot-cli/main.test.ts +++ b/registry/coder-labs/modules/copilot-cli/main.test.ts @@ -255,6 +255,26 @@ describe("copilot-cli module", async () => { expect(statusSlugEnv).toBeDefined(); }); + it("supports system prompt configuration", async () => { + const systemPromptVars = { + ...requiredVars, + system_prompt: + "You are a helpful AI assistant that focuses on code quality and best practices.", + }; + + const state = await runTerraformApply(moduleDir, systemPromptVars); + + const statusSlugEnv = findResourceInstance( + state, + "coder_env", + "mcp_app_status_slug", + ); + expect(statusSlugEnv).toBeDefined(); + + // System prompt is now handled via .github/copilot-instructions.md file creation + // during install, not via environment variables + }); + it("works with full configuration", async () => { const state = await runTerraformApply(moduleDir, fullConfigVars); diff --git a/registry/coder-labs/modules/copilot-cli/main.tf b/registry/coder-labs/modules/copilot-cli/main.tf index fdcf392fc..1b4b5f80c 100644 --- a/registry/coder-labs/modules/copilot-cli/main.tf +++ b/registry/coder-labs/modules/copilot-cli/main.tf @@ -46,6 +46,12 @@ variable "ai_prompt" { default = "" } +variable "system_prompt" { + type = string + description = "The system prompt to use for the Copilot CLI server." + default = "You are a helpful AI assistant that helps with coding tasks. Always provide clear explanations and follow best practices. Send a task status update to notify the user that you are ready for input, and then wait for user input." +} + variable "trusted_directories" { type = list(string) description = "Additional directories to trust for Copilot CLI operations." @@ -186,6 +192,7 @@ resource "coder_env" "copilot_model" { } + module "agentapi" { source = "registry.coder.com/coder/agentapi/coder" version = "1.1.1" @@ -216,6 +223,7 @@ module "agentapi" { ARG_WORKDIR='${local.workdir}' \ ARG_AI_PROMPT='${base64encode(var.ai_prompt)}' \ + ARG_SYSTEM_PROMPT='${base64encode(var.system_prompt)}' \ ARG_COPILOT_MODEL='${var.copilot_model}' \ ARG_ALLOW_ALL_TOOLS='${var.allow_all_tools}' \ ARG_ALLOW_TOOLS='${join(",", var.allow_tools)}' \ diff --git a/registry/coder-labs/modules/copilot-cli/scripts/start.sh b/registry/coder-labs/modules/copilot-cli/scripts/start.sh index 9651c550b..a344c3eb3 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/start.sh +++ b/registry/coder-labs/modules/copilot-cli/scripts/start.sh @@ -10,6 +10,7 @@ command_exists() { ARG_WORKDIR=${ARG_WORKDIR:-"$HOME"} ARG_AI_PROMPT=$(echo -n "${ARG_AI_PROMPT:-}" | base64 -d 2> /dev/null || echo "") +ARG_SYSTEM_PROMPT=$(echo -n "${ARG_SYSTEM_PROMPT:-}" | base64 -d 2> /dev/null || echo "") ARG_COPILOT_MODEL=${ARG_COPILOT_MODEL:-} ARG_ALLOW_ALL_TOOLS=${ARG_ALLOW_ALL_TOOLS:-false} ARG_ALLOW_TOOLS=${ARG_ALLOW_TOOLS:-} @@ -25,8 +26,17 @@ validate_copilot_installation() { build_copilot_args() { local args=() + local combined_prompt="" - if [ -n "$ARG_AI_PROMPT" ]; then + # Combine system prompt with AI prompt if both exist + if [ -n "$ARG_SYSTEM_PROMPT" ] && [ -n "$ARG_AI_PROMPT" ]; then + combined_prompt="$ARG_SYSTEM_PROMPT + +Task: $ARG_AI_PROMPT" + args+=(--prompt "$combined_prompt") + elif [ -n "$ARG_SYSTEM_PROMPT" ]; then + args+=(--prompt "$ARG_SYSTEM_PROMPT") + elif [ -n "$ARG_AI_PROMPT" ]; then args+=(--prompt "$ARG_AI_PROMPT") fi From 151e811ab9318ee950e8787d8e2305ee2cec7f27 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Tue, 30 Sep 2025 17:02:52 -0500 Subject: [PATCH 05/58] fix: remove unused GitHub external auth data block --- registry/coder-labs/modules/copilot-cli/main.tf | 3 --- 1 file changed, 3 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/main.tf b/registry/coder-labs/modules/copilot-cli/main.tf index 1b4b5f80c..83007f400 100644 --- a/registry/coder-labs/modules/copilot-cli/main.tf +++ b/registry/coder-labs/modules/copilot-cli/main.tf @@ -157,9 +157,6 @@ variable "post_install_script" { data "coder_workspace" "me" {} data "coder_workspace_owner" "me" {} -data "coder_external_auth" "github" { - id = var.external_auth_id -} locals { workdir = trimsuffix(var.workdir, "/") From ca07248401380e9e67c3d83c61b3ab13d092cce6 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Tue, 30 Sep 2025 17:19:16 -0500 Subject: [PATCH 06/58] feat: add back installation step --- .../coder-labs/modules/copilot-cli/README.md | 2 -- .../modules/copilot-cli/scripts/install.sh | 22 ++++++++++++++----- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/README.md b/registry/coder-labs/modules/copilot-cli/README.md index fcbf361cf..3ac1dd3b5 100644 --- a/registry/coder-labs/modules/copilot-cli/README.md +++ b/registry/coder-labs/modules/copilot-cli/README.md @@ -25,7 +25,6 @@ module "copilot_cli" { ## Prerequisites - **Node.js v22+** and **npm v10+** -- **GitHub Copilot CLI**: `npm install -g @github/copilot` - **Active Copilot subscription** (GitHub Copilot Pro, Pro+, Business, or Enterprise) - **GitHub external authentication** configured in Coder @@ -73,7 +72,6 @@ module "copilot_cli" { #!/bin/bash curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - sudo apt-get install -y nodejs - npm install -g @github/copilot EOT } ``` diff --git a/registry/coder-labs/modules/copilot-cli/scripts/install.sh b/registry/coder-labs/modules/copilot-cli/scripts/install.sh index f8e43caeb..d30b6a2d5 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/install.sh +++ b/registry/coder-labs/modules/copilot-cli/scripts/install.sh @@ -26,17 +26,28 @@ validate_prerequisites() { exit 1 fi - if ! command_exists copilot; then - echo "ERROR: Copilot CLI not installed. Run: npm install -g @github/copilot" - exit 1 - fi - node_version=$(node --version | sed 's/v//' | cut -d. -f1) if [ "$node_version" -lt 22 ]; then echo "WARNING: Node.js v$node_version detected. Copilot CLI requires v22+." fi } +install_copilot_cli() { + if ! command_exists copilot; then + echo "Installing GitHub Copilot CLI..." + npm install -g @github/copilot + + if ! command_exists copilot; then + echo "ERROR: Failed to install Copilot CLI" + exit 1 + fi + + echo "GitHub Copilot CLI installed successfully" + else + echo "GitHub Copilot CLI already installed" + fi +} + check_github_authentication() { echo "Checking GitHub authentication via Coder external auth..." @@ -114,6 +125,7 @@ configure_coder_integration() { } validate_prerequisites +install_copilot_cli check_github_authentication setup_copilot_configurations configure_coder_integration From b880e243411911e2269fae2fef2ff2e0b87f999d Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Tue, 30 Sep 2025 17:32:39 -0500 Subject: [PATCH 07/58] chore: streamline argument handling in build_copilot_args function --- .../modules/copilot-cli/scripts/start.sh | 33 +++++++------------ 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/scripts/start.sh b/registry/coder-labs/modules/copilot-cli/scripts/start.sh index a344c3eb3..764fc5cfd 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/start.sh +++ b/registry/coder-labs/modules/copilot-cli/scripts/start.sh @@ -25,30 +25,29 @@ validate_copilot_installation() { } build_copilot_args() { - local args=() - local combined_prompt="" + COPILOT_ARGS=() # Combine system prompt with AI prompt if both exist if [ -n "$ARG_SYSTEM_PROMPT" ] && [ -n "$ARG_AI_PROMPT" ]; then - combined_prompt="$ARG_SYSTEM_PROMPT + local combined_prompt="$ARG_SYSTEM_PROMPT Task: $ARG_AI_PROMPT" - args+=(--prompt "$combined_prompt") + COPILOT_ARGS+=(--prompt "$combined_prompt") elif [ -n "$ARG_SYSTEM_PROMPT" ]; then - args+=(--prompt "$ARG_SYSTEM_PROMPT") + COPILOT_ARGS+=(--prompt "$ARG_SYSTEM_PROMPT") elif [ -n "$ARG_AI_PROMPT" ]; then - args+=(--prompt "$ARG_AI_PROMPT") + COPILOT_ARGS+=(--prompt "$ARG_AI_PROMPT") fi if [ "$ARG_ALLOW_ALL_TOOLS" = "true" ]; then - args+=(--allow-all-tools) + COPILOT_ARGS+=(--allow-all-tools) fi if [ -n "$ARG_ALLOW_TOOLS" ]; then IFS=',' read -ra ALLOW_ARRAY <<< "$ARG_ALLOW_TOOLS" for tool in "${ALLOW_ARRAY[@]}"; do if [ -n "$tool" ]; then - args+=(--allow-tool "$tool") + COPILOT_ARGS+=(--allow-tool "$tool") fi done fi @@ -57,12 +56,10 @@ Task: $ARG_AI_PROMPT" IFS=',' read -ra DENY_ARRAY <<< "$ARG_DENY_TOOLS" for tool in "${DENY_ARRAY[@]}"; do if [ -n "$tool" ]; then - args+=(--deny-tool "$tool") + COPILOT_ARGS+=(--deny-tool "$tool") fi done fi - - echo "${args[@]}" } configure_copilot_model() { @@ -88,17 +85,11 @@ start_agentapi() { echo "Starting in directory: $ARG_WORKDIR" cd "$ARG_WORKDIR" - local copilot_args_str - copilot_args_str=$(build_copilot_args) - - local copilot_args=() - if [ -n "$copilot_args_str" ]; then - read -ra copilot_args <<< "$copilot_args_str" - fi + build_copilot_args - if [ ${#copilot_args[@]} -gt 0 ]; then - echo "Copilot arguments: ${copilot_args[*]}" - agentapi server --type claude --term-width 120 --term-height 40 -- copilot "${copilot_args[@]}" + if [ ${#COPILOT_ARGS[@]} -gt 0 ]; then + echo "Copilot arguments: ${COPILOT_ARGS[*]}" + agentapi server --type claude --term-width 120 --term-height 40 -- copilot "${COPILOT_ARGS[@]}" else echo "Starting Copilot CLI in interactive mode" agentapi server --type claude --term-width 120 --term-height 40 -- copilot From 3cef7332c086859ca2c33dce7e72f2c00b477967 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Tue, 30 Sep 2025 18:05:21 -0500 Subject: [PATCH 08/58] feat: add external auth ID handling and GitHub authentication setup --- .../coder-labs/modules/copilot-cli/main.tf | 1 + .../modules/copilot-cli/scripts/start.sh | 28 ++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/registry/coder-labs/modules/copilot-cli/main.tf b/registry/coder-labs/modules/copilot-cli/main.tf index 83007f400..ed92e8ca2 100644 --- a/registry/coder-labs/modules/copilot-cli/main.tf +++ b/registry/coder-labs/modules/copilot-cli/main.tf @@ -226,6 +226,7 @@ module "agentapi" { ARG_ALLOW_TOOLS='${join(",", var.allow_tools)}' \ ARG_DENY_TOOLS='${join(",", var.deny_tools)}' \ ARG_TRUSTED_DIRECTORIES='${join(",", var.trusted_directories)}' \ + ARG_EXTERNAL_AUTH_ID='${var.external_auth_id}' \ /tmp/start.sh EOT diff --git a/registry/coder-labs/modules/copilot-cli/scripts/start.sh b/registry/coder-labs/modules/copilot-cli/scripts/start.sh index 764fc5cfd..d5d28b6d0 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/start.sh +++ b/registry/coder-labs/modules/copilot-cli/scripts/start.sh @@ -16,6 +16,7 @@ ARG_ALLOW_ALL_TOOLS=${ARG_ALLOW_ALL_TOOLS:-false} ARG_ALLOW_TOOLS=${ARG_ALLOW_TOOLS:-} ARG_DENY_TOOLS=${ARG_DENY_TOOLS:-} ARG_TRUSTED_DIRECTORIES=${ARG_TRUSTED_DIRECTORIES:-} +ARG_EXTERNAL_AUTH_ID=${ARG_EXTERNAL_AUTH_ID:-github} validate_copilot_installation() { if ! command_exists copilot; then @@ -81,6 +82,31 @@ configure_copilot_model() { fi } +setup_github_authentication() { + echo "Setting up GitHub authentication..." + + if command_exists coder; then + local github_token + if github_token=$(coder external-auth access-token "${ARG_EXTERNAL_AUTH_ID}" 2>/dev/null); then + if [ -n "$github_token" ] && [ "$github_token" != "null" ]; then + export GITHUB_TOKEN="$github_token" + export GH_TOKEN="$github_token" + echo "GitHub authentication: via Coder external auth" + return 0 + fi + fi + fi + + if command_exists gh && gh auth status > /dev/null 2>&1; then + echo "GitHub authentication: via GitHub CLI" + return 0 + fi + + echo "WARNING: No GitHub authentication found. Copilot CLI requires authentication." + echo "Please ensure GitHub external auth is configured in Coder or run 'gh auth login'" + return 1 +} + start_agentapi() { echo "Starting in directory: $ARG_WORKDIR" cd "$ARG_WORKDIR" @@ -99,7 +125,7 @@ start_agentapi() { configure_copilot_model echo "COPILOT_MODEL=${ARG_COPILOT_MODEL:-${COPILOT_MODEL:-not set}}" -echo "GitHub authentication: via Coder external auth" +setup_github_authentication validate_copilot_installation start_agentapi From ab1e663f92f75aabe467cfaeada2adf200e26d91 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Tue, 30 Sep 2025 20:44:36 -0500 Subject: [PATCH 09/58] feat(copilot-cli): improve GitHub authentication with multiple fallback methods --- .../coder-labs/modules/copilot-cli/README.md | 21 +++++++++++++----- .../modules/copilot-cli/scripts/install.sh | 22 +++++++++++-------- .../modules/copilot-cli/scripts/start.sh | 22 ++++++++++++++----- 3 files changed, 44 insertions(+), 21 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/README.md b/registry/coder-labs/modules/copilot-cli/README.md index 3ac1dd3b5..43803a992 100644 --- a/registry/coder-labs/modules/copilot-cli/README.md +++ b/registry/coder-labs/modules/copilot-cli/README.md @@ -26,7 +26,11 @@ module "copilot_cli" { - **Node.js v22+** and **npm v10+** - **Active Copilot subscription** (GitHub Copilot Pro, Pro+, Business, or Enterprise) -- **GitHub external authentication** configured in Coder +- **GitHub authentication** via one of: + - Coder external authentication (recommended) + - GitHub CLI (`gh auth login`) + - Environment token (`GITHUB_TOKEN`) + - Or use interactive login in Copilot CLI ## Examples @@ -140,12 +144,17 @@ module "copilot_cli" { ## Authentication -This module uses Coder's GitHub external authentication: +This module works with multiple GitHub authentication methods: -- Users authenticate via GitHub OAuth in the Coder UI -- Copilot CLI automatically uses the authenticated session -- No manual token management required -- If not authenticated, users will be prompted to login via the `/login` slash command +**Recommended (automatic):** +- **Coder External Auth**: Configure GitHub external authentication in Coder for seamless OAuth token integration +- **GitHub CLI**: Users can run `gh auth login` in their workspace + +**Automatic fallback:** +- **Environment tokens**: Uses existing `GITHUB_TOKEN` if available (note: Personal Access Tokens may not work with all Copilot CLI features) +- **Interactive login**: If no authentication is found, Copilot CLI will prompt users to login via the `/login` slash command + +**No setup required** - the module automatically detects and uses whatever authentication is available. ## Troubleshooting diff --git a/registry/coder-labs/modules/copilot-cli/scripts/install.sh b/registry/coder-labs/modules/copilot-cli/scripts/install.sh index d30b6a2d5..b82c4b822 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/install.sh +++ b/registry/coder-labs/modules/copilot-cli/scripts/install.sh @@ -49,26 +49,30 @@ install_copilot_cli() { } check_github_authentication() { - echo "Checking GitHub authentication via Coder external auth..." + echo "Checking GitHub authentication..." if command_exists coder; then if coder external-auth access-token "${ARG_EXTERNAL_AUTH_ID:-github}" > /dev/null 2>&1; then - echo "GitHub authentication via Coder external auth: OK" + echo "✓ GitHub OAuth authentication via Coder external auth" return 0 - else - echo "WARNING: GitHub external auth not configured or expired" - echo "Please authenticate with GitHub in the Coder UI" fi fi if command_exists gh && gh auth status > /dev/null 2>&1; then - echo "GitHub CLI authentication detected as fallback" + echo "✓ GitHub OAuth authentication via GitHub CLI" return 0 fi - echo "WARNING: No GitHub authentication found. Copilot CLI requires:" - echo " - GitHub external authentication configured in Coder (recommended)" - echo " - Or GitHub CLI with 'gh auth login'" + if [ -n "$GITHUB_TOKEN" ]; then + echo "✓ GitHub token found in environment" + echo " Note: Copilot CLI works best with OAuth tokens from Coder external auth or 'gh auth login'" + return 0 + fi + + echo "⚠ No GitHub authentication detected" + echo " Copilot CLI will prompt for authentication when started" + echo " For seamless experience, configure GitHub external auth in Coder or run 'gh auth login'" + return 0 } setup_copilot_configurations() { diff --git a/registry/coder-labs/modules/copilot-cli/scripts/start.sh b/registry/coder-labs/modules/copilot-cli/scripts/start.sh index d5d28b6d0..2df81298d 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/start.sh +++ b/registry/coder-labs/modules/copilot-cli/scripts/start.sh @@ -87,24 +87,34 @@ setup_github_authentication() { if command_exists coder; then local github_token - if github_token=$(coder external-auth access-token "${ARG_EXTERNAL_AUTH_ID}" 2>/dev/null); then + if github_token=$(coder external-auth access-token "${ARG_EXTERNAL_AUTH_ID:-github}" 2>/dev/null); then if [ -n "$github_token" ] && [ "$github_token" != "null" ]; then export GITHUB_TOKEN="$github_token" export GH_TOKEN="$github_token" - echo "GitHub authentication: via Coder external auth" + echo "✓ Using Coder external auth OAuth token" return 0 fi fi fi + # Try GitHub CLI as fallback if command_exists gh && gh auth status > /dev/null 2>&1; then - echo "GitHub authentication: via GitHub CLI" + echo "✓ Using GitHub CLI OAuth authentication" return 0 fi - echo "WARNING: No GitHub authentication found. Copilot CLI requires authentication." - echo "Please ensure GitHub external auth is configured in Coder or run 'gh auth login'" - return 1 + # Use existing environment variable if present + if [ -n "$GITHUB_TOKEN" ]; then + export GH_TOKEN="$GITHUB_TOKEN" + echo "✓ Using GitHub token from environment" + echo " Note: If this is a Personal Access Token, Copilot CLI may not work properly" + return 0 + fi + + echo "⚠ No GitHub authentication available" + echo " Copilot CLI will prompt for login during first use" + echo " Use the '/login' command in Copilot CLI to authenticate" + return 0 # Don't fail - let Copilot CLI handle authentication } start_agentapi() { From 673b2017ceb35dd212029dba43fea9016e13cacf Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Tue, 30 Sep 2025 21:20:53 -0500 Subject: [PATCH 10/58] feat: enhance GitHub authentication with direct token support and improved fallback methods --- .../coder-labs/modules/copilot-cli/README.md | 37 +++++++++++++++---- .../coder-labs/modules/copilot-cli/main.tf | 14 +++++++ .../modules/copilot-cli/scripts/install.sh | 11 +++--- .../modules/copilot-cli/scripts/start.sh | 26 ++++++------- 4 files changed, 61 insertions(+), 27 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/README.md b/registry/coder-labs/modules/copilot-cli/README.md index 43803a992..820c04f77 100644 --- a/registry/coder-labs/modules/copilot-cli/README.md +++ b/registry/coder-labs/modules/copilot-cli/README.md @@ -27,9 +27,9 @@ module "copilot_cli" { - **Node.js v22+** and **npm v10+** - **Active Copilot subscription** (GitHub Copilot Pro, Pro+, Business, or Enterprise) - **GitHub authentication** via one of: + - Direct token via `github_token` variable (highest priority) - Coder external authentication (recommended) - GitHub CLI (`gh auth login`) - - Environment token (`GITHUB_TOKEN`) - Or use interactive login in Copilot CLI ## Examples @@ -80,6 +80,20 @@ module "copilot_cli" { } ``` +### Direct Token Authentication + +Use a GitHub token directly (OAuth token or Personal Access Token): + +```tf +module "copilot_cli" { + source = "registry.coder.com/coder-labs/copilot-cli/coder" + version = "1.0.0" + agent_id = coder_agent.example.id + workdir = "/home/coder/project" + github_token = "your_github_token_here" # Or use data.coder_external_auth.github.access_token +} +``` + ### Standalone Mode Run and configure Copilot CLI as a standalone tool in your workspace. @@ -144,17 +158,24 @@ module "copilot_cli" { ## Authentication -This module works with multiple GitHub authentication methods: +This module works with multiple GitHub authentication methods in priority order: + +**1. Direct Token (highest priority):** + +- **`github_token` variable**: Provide a GitHub OAuth token or Personal Access Token directly to the module -**Recommended (automatic):** -- **Coder External Auth**: Configure GitHub external authentication in Coder for seamless OAuth token integration -- **GitHub CLI**: Users can run `gh auth login` in their workspace +**2. Automatic detection:** + +- **Coder External Auth**: OAuth tokens from GitHub external authentication configured in Coder +- **GitHub CLI**: OAuth tokens from `gh auth login` in the workspace + +**3. Interactive fallback:** -**Automatic fallback:** -- **Environment tokens**: Uses existing `GITHUB_TOKEN` if available (note: Personal Access Tokens may not work with all Copilot CLI features) - **Interactive login**: If no authentication is found, Copilot CLI will prompt users to login via the `/login` slash command -**No setup required** - the module automatically detects and uses whatever authentication is available. +**No setup required** for automatic methods - the module detects and uses whatever authentication is available. + +> **Note**: OAuth tokens work best with Copilot CLI. Personal Access Tokens may have limited functionality. ## Troubleshooting diff --git a/registry/coder-labs/modules/copilot-cli/main.tf b/registry/coder-labs/modules/copilot-cli/main.tf index ed92e8ca2..f4d3a7835 100644 --- a/registry/coder-labs/modules/copilot-cli/main.tf +++ b/registry/coder-labs/modules/copilot-cli/main.tf @@ -24,6 +24,13 @@ variable "external_auth_id" { default = "github" } +variable "github_token" { + type = string + description = "GitHub OAuth token or Personal Access Token. If provided, this will be used instead of auto-detecting authentication." + default = "" + sensitive = true +} + variable "copilot_model" { type = string description = "Model to use. Supported values: claude-sonnet-4 (default), claude-sonnet-4.5, gpt-5." @@ -188,6 +195,13 @@ resource "coder_env" "copilot_model" { value = var.copilot_model } +resource "coder_env" "github_token" { + count = var.github_token != "" ? 1 : 0 + agent_id = var.agent_id + name = "GITHUB_TOKEN" + value = var.github_token +} + module "agentapi" { diff --git a/registry/coder-labs/modules/copilot-cli/scripts/install.sh b/registry/coder-labs/modules/copilot-cli/scripts/install.sh index b82c4b822..82954b869 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/install.sh +++ b/registry/coder-labs/modules/copilot-cli/scripts/install.sh @@ -51,6 +51,11 @@ install_copilot_cli() { check_github_authentication() { echo "Checking GitHub authentication..." + if [ -n "$GITHUB_TOKEN" ]; then + echo "✓ GitHub token provided via module configuration" + return 0 + fi + if command_exists coder; then if coder external-auth access-token "${ARG_EXTERNAL_AUTH_ID:-github}" > /dev/null 2>&1; then echo "✓ GitHub OAuth authentication via Coder external auth" @@ -63,12 +68,6 @@ check_github_authentication() { return 0 fi - if [ -n "$GITHUB_TOKEN" ]; then - echo "✓ GitHub token found in environment" - echo " Note: Copilot CLI works best with OAuth tokens from Coder external auth or 'gh auth login'" - return 0 - fi - echo "⚠ No GitHub authentication detected" echo " Copilot CLI will prompt for authentication when started" echo " For seamless experience, configure GitHub external auth in Coder or run 'gh auth login'" diff --git a/registry/coder-labs/modules/copilot-cli/scripts/start.sh b/registry/coder-labs/modules/copilot-cli/scripts/start.sh index 2df81298d..d7923fc20 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/start.sh +++ b/registry/coder-labs/modules/copilot-cli/scripts/start.sh @@ -84,10 +84,18 @@ configure_copilot_model() { setup_github_authentication() { echo "Setting up GitHub authentication..." - + + # Check for provided token first (highest priority) + if [ -n "$GITHUB_TOKEN" ]; then + export GH_TOKEN="$GITHUB_TOKEN" + echo "✓ Using GitHub token from module configuration" + return 0 + fi + + # Try external auth if command_exists coder; then local github_token - if github_token=$(coder external-auth access-token "${ARG_EXTERNAL_AUTH_ID:-github}" 2>/dev/null); then + if github_token=$(coder external-auth access-token "${ARG_EXTERNAL_AUTH_ID:-github}" 2> /dev/null); then if [ -n "$github_token" ] && [ "$github_token" != "null" ]; then export GITHUB_TOKEN="$github_token" export GH_TOKEN="$github_token" @@ -96,25 +104,17 @@ setup_github_authentication() { fi fi fi - + # Try GitHub CLI as fallback if command_exists gh && gh auth status > /dev/null 2>&1; then echo "✓ Using GitHub CLI OAuth authentication" return 0 fi - - # Use existing environment variable if present - if [ -n "$GITHUB_TOKEN" ]; then - export GH_TOKEN="$GITHUB_TOKEN" - echo "✓ Using GitHub token from environment" - echo " Note: If this is a Personal Access Token, Copilot CLI may not work properly" - return 0 - fi - + echo "⚠ No GitHub authentication available" echo " Copilot CLI will prompt for login during first use" echo " Use the '/login' command in Copilot CLI to authenticate" - return 0 # Don't fail - let Copilot CLI handle authentication + return 0 # Don't fail - let Copilot CLI handle authentication } start_agentapi() { From 3737c1a2079651b03267c4422f08a916938f5d14 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Tue, 30 Sep 2025 21:24:27 -0500 Subject: [PATCH 11/58] chore: bun run fmt --- registry/coder-labs/modules/copilot-cli/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/coder-labs/modules/copilot-cli/README.md b/registry/coder-labs/modules/copilot-cli/README.md index 820c04f77..55662657f 100644 --- a/registry/coder-labs/modules/copilot-cli/README.md +++ b/registry/coder-labs/modules/copilot-cli/README.md @@ -160,7 +160,7 @@ module "copilot_cli" { This module works with multiple GitHub authentication methods in priority order: -**1. Direct Token (highest priority):** +**1. Direct Token :** - **`github_token` variable**: Provide a GitHub OAuth token or Personal Access Token directly to the module From a62146f05e5d48935365d58800aeea4515d53b3d Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Tue, 30 Sep 2025 22:29:07 -0500 Subject: [PATCH 12/58] feat: enhance task reporting integration and support for GitHub token in Copilot CLI --- .../coder-labs/modules/copilot-cli/README.md | 17 ++++ .../modules/copilot-cli/main.test.ts | 25 +++++ .../modules/copilot-cli/scripts/install.sh | 98 ++++++++++++++++++- .../modules/copilot-cli/scripts/start.sh | 20 +++- 4 files changed, 155 insertions(+), 5 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/README.md b/registry/coder-labs/modules/copilot-cli/README.md index 55662657f..d66cb8c38 100644 --- a/registry/coder-labs/modules/copilot-cli/README.md +++ b/registry/coder-labs/modules/copilot-cli/README.md @@ -177,6 +177,23 @@ This module works with multiple GitHub authentication methods in priority order: > **Note**: OAuth tokens work best with Copilot CLI. Personal Access Tokens may have limited functionality. +## Task Reporting + +When `report_tasks = true` (default), this module automatically configures the **Coder MCP server** for task reporting integration: + +- **Automatic Integration**: The Coder MCP server is added to the MCP configuration automatically +- **Task Status Updates**: Copilot CLI can report task progress to the Coder UI +- **No Manual Setup**: Works out-of-the-box with Coder's task reporting system +- **Custom MCP Compatible**: If you provide custom `mcp_config`, the Coder MCP server is added alongside your custom servers + +The Coder MCP server enables Copilot CLI to: + +- Report task status (working, complete, failure) +- Send progress updates to the Coder dashboard +- Integrate with Coder's AI task workflow system + +To disable task reporting, set `report_tasks = false`. + ## Troubleshooting If you encounter any issues, check the log files in the `~/.copilot-module` directory within your workspace for detailed information. diff --git a/registry/coder-labs/modules/copilot-cli/main.test.ts b/registry/coder-labs/modules/copilot-cli/main.test.ts index 3b29907bc..645f4d179 100644 --- a/registry/coder-labs/modules/copilot-cli/main.test.ts +++ b/registry/coder-labs/modules/copilot-cli/main.test.ts @@ -287,4 +287,29 @@ describe("copilot-cli module", async () => { expect(statusSlugEnv.name).toBe("CODER_MCP_APP_STATUS_SLUG"); expect(statusSlugEnv.value).toBe("copilot-cli"); }); + + it("supports github_token variable", async () => { + const tokenVars = { + ...requiredVars, + github_token: "test_github_token_123", + }; + + const state = await runTerraformApply(moduleDir, tokenVars); + + const statusSlugEnv = findResourceInstance( + state, + "coder_env", + "mcp_app_status_slug", + ); + expect(statusSlugEnv).toBeDefined(); + + const githubTokenEnv = findResourceInstance( + state, + "coder_env", + "github_token", + ); + expect(githubTokenEnv).toBeDefined(); + expect(githubTokenEnv.name).toBe("GITHUB_TOKEN"); + expect(githubTokenEnv.value).toBe("test_github_token_123"); + }); }); diff --git a/registry/coder-labs/modules/copilot-cli/scripts/install.sh b/registry/coder-labs/modules/copilot-cli/scripts/install.sh index 82954b869..db8be895e 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/install.sh +++ b/registry/coder-labs/modules/copilot-cli/scripts/install.sh @@ -83,17 +83,111 @@ setup_copilot_configurations() { if [ -n "$ARG_MCP_CONFIG" ]; then echo "Configuring custom MCP servers..." - echo "$ARG_MCP_CONFIG" > "$module_path/mcp_config.json" + if command_exists jq; then + echo "$ARG_MCP_CONFIG" | jq ' + .mcpServers = (.mcpServers // {}) | + if .mcpServers.github == null then + .mcpServers.github = {"command": "@github/copilot-mcp-github"} + else . end | + if "'"$ARG_REPORT_TASKS"'" == "true" then + .mcpServers.coder = { + "command": "coder", + "args": ["exp", "mcp", "server"], + "type": "stdio", + "env": { + "CODER_MCP_APP_STATUS_SLUG": "'"$ARG_MCP_APP_STATUS_SLUG"'", + "CODER_MCP_AI_AGENTAPI_URL": "http://localhost:3284" + } + } + else . end + ' > "$module_path/mcp_config.json" + elif command_exists node; then + node -e " + const config = JSON.parse(\`$ARG_MCP_CONFIG\`); + config.mcpServers = config.mcpServers || {}; + + if (!config.mcpServers.github) { + config.mcpServers.github = { + command: '@github/copilot-mcp-github' + }; + } + + if ('$ARG_REPORT_TASKS' === 'true') { + config.mcpServers.coder = { + command: 'coder', + args: ['exp', 'mcp', 'server'], + type: 'stdio', + env: { + CODER_MCP_APP_STATUS_SLUG: '$ARG_MCP_APP_STATUS_SLUG', + CODER_MCP_AI_AGENTAPI_URL: 'http://localhost:3284' + } + }; + } + + console.log(JSON.stringify(config, null, 2)); + " > "$module_path/mcp_config.json" + else + if [ "$ARG_REPORT_TASKS" = "true" ]; then + echo "$ARG_MCP_CONFIG" | sed 's/}$//' > "$module_path/mcp_config.json" + cat >> "$module_path/mcp_config.json" << EOF + "github": { + "command": "@github/copilot-mcp-github" + }, + "coder": { + "command": "coder", + "args": ["exp", "mcp", "server"], + "type": "stdio", + "env": { + "CODER_MCP_APP_STATUS_SLUG": "$ARG_MCP_APP_STATUS_SLUG", + "CODER_MCP_AI_AGENTAPI_URL": "http://localhost:3284" + } + } + } +} +EOF + else + echo "$ARG_MCP_CONFIG" | sed 's/}$//' > "$module_path/mcp_config.json" + cat >> "$module_path/mcp_config.json" << EOF + "github": { + "command": "@github/copilot-mcp-github" + } + } +} +EOF + fi + fi else - cat > "$module_path/mcp_config.json" << 'EOF' + if [ "$ARG_REPORT_TASKS" = "true" ]; then + echo "Configuring default MCP servers with Coder task reporting..." + cat > "$module_path/mcp_config.json" << EOF { "mcpServers": { "github": { "command": "@github/copilot-mcp-github" + }, + "coder": { + "command": "coder", + "args": ["exp", "mcp", "server"], + "type": "stdio", + "env": { + "CODER_MCP_APP_STATUS_SLUG": "$ARG_MCP_APP_STATUS_SLUG", + "CODER_MCP_AI_AGENTAPI_URL": "http://localhost:3284" + } } } } EOF + else + cat > "$module_path/mcp_config.json" << 'EOF' +{ + "mcpServers": { + "github": { + "command": "@github/copilot-mcp-github" + } + } +} +EOF + fi fi setup_copilot_config diff --git a/registry/coder-labs/modules/copilot-cli/scripts/start.sh b/registry/coder-labs/modules/copilot-cli/scripts/start.sh index d7923fc20..eefee7288 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/start.sh +++ b/registry/coder-labs/modules/copilot-cli/scripts/start.sh @@ -123,12 +123,26 @@ start_agentapi() { build_copilot_args + local mcp_args=() + local module_path="$HOME/.copilot-module" + + if [ -f "$module_path/mcp_config.json" ]; then + mcp_args+=(--mcp-config "$module_path/mcp_config.json") + fi + if [ ${#COPILOT_ARGS[@]} -gt 0 ]; then echo "Copilot arguments: ${COPILOT_ARGS[*]}" - agentapi server --type claude --term-width 120 --term-height 40 -- copilot "${COPILOT_ARGS[@]}" + if [ ${#mcp_args[@]} -gt 0 ]; then + agentapi server --type claude --term-width 120 --term-height 40 "${mcp_args[@]}" -- copilot "${COPILOT_ARGS[@]}" + else + agentapi server --type claude --term-width 120 --term-height 40 -- copilot "${COPILOT_ARGS[@]}" + fi else - echo "Starting Copilot CLI in interactive mode" - agentapi server --type claude --term-width 120 --term-height 40 -- copilot + if [ ${#mcp_args[@]} -gt 0 ]; then + agentapi server --type claude --term-width 120 --term-height 40 "${mcp_args[@]}" -- copilot + else + agentapi server --type claude --term-width 120 --term-height 40 -- copilot + fi fi } From 659a6b2002955d566d9ff45a3fe1eab04fa26456 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Tue, 30 Sep 2025 23:04:31 -0500 Subject: [PATCH 13/58] fix: streamline MCP server configuration --- .../modules/copilot-cli/scripts/install.sh | 228 +++++++++--------- .../modules/copilot-cli/scripts/start.sh | 25 +- 2 files changed, 120 insertions(+), 133 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/scripts/install.sh b/registry/coder-labs/modules/copilot-cli/scripts/install.sh index db8be895e..20ea8fe7a 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/install.sh +++ b/registry/coder-labs/modules/copilot-cli/scripts/install.sh @@ -81,115 +81,6 @@ setup_copilot_configurations() { mkdir -p "$module_path" mkdir -p "$HOME/.config" - if [ -n "$ARG_MCP_CONFIG" ]; then - echo "Configuring custom MCP servers..." - if command_exists jq; then - echo "$ARG_MCP_CONFIG" | jq ' - .mcpServers = (.mcpServers // {}) | - if .mcpServers.github == null then - .mcpServers.github = {"command": "@github/copilot-mcp-github"} - else . end | - if "'"$ARG_REPORT_TASKS"'" == "true" then - .mcpServers.coder = { - "command": "coder", - "args": ["exp", "mcp", "server"], - "type": "stdio", - "env": { - "CODER_MCP_APP_STATUS_SLUG": "'"$ARG_MCP_APP_STATUS_SLUG"'", - "CODER_MCP_AI_AGENTAPI_URL": "http://localhost:3284" - } - } - else . end - ' > "$module_path/mcp_config.json" - elif command_exists node; then - node -e " - const config = JSON.parse(\`$ARG_MCP_CONFIG\`); - config.mcpServers = config.mcpServers || {}; - - if (!config.mcpServers.github) { - config.mcpServers.github = { - command: '@github/copilot-mcp-github' - }; - } - - if ('$ARG_REPORT_TASKS' === 'true') { - config.mcpServers.coder = { - command: 'coder', - args: ['exp', 'mcp', 'server'], - type: 'stdio', - env: { - CODER_MCP_APP_STATUS_SLUG: '$ARG_MCP_APP_STATUS_SLUG', - CODER_MCP_AI_AGENTAPI_URL: 'http://localhost:3284' - } - }; - } - - console.log(JSON.stringify(config, null, 2)); - " > "$module_path/mcp_config.json" - else - if [ "$ARG_REPORT_TASKS" = "true" ]; then - echo "$ARG_MCP_CONFIG" | sed 's/}$//' > "$module_path/mcp_config.json" - cat >> "$module_path/mcp_config.json" << EOF - "github": { - "command": "@github/copilot-mcp-github" - }, - "coder": { - "command": "coder", - "args": ["exp", "mcp", "server"], - "type": "stdio", - "env": { - "CODER_MCP_APP_STATUS_SLUG": "$ARG_MCP_APP_STATUS_SLUG", - "CODER_MCP_AI_AGENTAPI_URL": "http://localhost:3284" - } - } - } -} -EOF - else - echo "$ARG_MCP_CONFIG" | sed 's/}$//' > "$module_path/mcp_config.json" - cat >> "$module_path/mcp_config.json" << EOF - "github": { - "command": "@github/copilot-mcp-github" - } - } -} -EOF - fi - fi - else - if [ "$ARG_REPORT_TASKS" = "true" ]; then - echo "Configuring default MCP servers with Coder task reporting..." - cat > "$module_path/mcp_config.json" << EOF -{ - "mcpServers": { - "github": { - "command": "@github/copilot-mcp-github" - }, - "coder": { - "command": "coder", - "args": ["exp", "mcp", "server"], - "type": "stdio", - "env": { - "CODER_MCP_APP_STATUS_SLUG": "$ARG_MCP_APP_STATUS_SLUG", - "CODER_MCP_AI_AGENTAPI_URL": "http://localhost:3284" - } - } - } -} -EOF - else - cat > "$module_path/mcp_config.json" << 'EOF' -{ - "mcpServers": { - "github": { - "command": "@github/copilot-mcp-github" - } - } -} -EOF - fi - fi - setup_copilot_config echo "$ARG_WORKDIR" > "$module_path/trusted_directories" @@ -199,8 +90,123 @@ setup_copilot_config() { local config_file="$HOME/.config/copilot.json" if [ -n "$ARG_COPILOT_CONFIG" ]; then - echo "Setting up Copilot configuration..." - echo "$ARG_COPILOT_CONFIG" > "$config_file" + echo "Setting up Copilot configuration with MCP servers..." + + if [ -n "$ARG_MCP_CONFIG" ]; then + echo "Merging custom MCP servers into Copilot configuration..." + if command_exists jq; then + local merged_config + merged_config=$(echo "$ARG_COPILOT_CONFIG" | jq --argjson mcp_config "$ARG_MCP_CONFIG" ' + . + { + mcpServers: ($mcp_config.mcpServers // {}) | + if .github == null then + .github = {"command": "@github/copilot-mcp-github"} + else . end | + if "'"$ARG_REPORT_TASKS"'" == "true" then + .coder = { + "command": "coder", + "args": ["exp", "mcp", "server"], + "type": "stdio", + "env": { + "CODER_MCP_APP_STATUS_SLUG": "'"$ARG_MCP_APP_STATUS_SLUG"'", + "CODER_MCP_AI_AGENTAPI_URL": "http://localhost:3284" + } + } + else . end + } + ') + echo "$merged_config" > "$config_file" + elif command_exists node; then + node -e " + const copilotConfig = JSON.parse(\`$ARG_COPILOT_CONFIG\`); + const mcpConfig = JSON.parse(\`$ARG_MCP_CONFIG\`); + + copilotConfig.mcpServers = mcpConfig.mcpServers || {}; + + if (!copilotConfig.mcpServers.github) { + copilotConfig.mcpServers.github = { + command: '@github/copilot-mcp-github' + }; + } + + if ('$ARG_REPORT_TASKS' === 'true') { + copilotConfig.mcpServers.coder = { + command: 'coder', + args: ['exp', 'mcp', 'server'], + type: 'stdio', + env: { + CODER_MCP_APP_STATUS_SLUG: '$ARG_MCP_APP_STATUS_SLUG', + CODER_MCP_AI_AGENTAPI_URL: 'http://localhost:3284' + } + }; + } + + console.log(JSON.stringify(copilotConfig, null, 2)); + " > "$config_file" + else + echo "$ARG_COPILOT_CONFIG" > "$config_file" + fi + else + if [ "$ARG_REPORT_TASKS" = "true" ]; then + echo "Adding default MCP servers with Coder task reporting..." + if command_exists jq; then + echo "$ARG_COPILOT_CONFIG" | jq '. + { + mcpServers: { + "github": { + "command": "@github/copilot-mcp-github" + }, + "coder": { + "command": "coder", + "args": ["exp", "mcp", "server"], + "type": "stdio", + "env": { + "CODER_MCP_APP_STATUS_SLUG": "'"$ARG_MCP_APP_STATUS_SLUG"'", + "CODER_MCP_AI_AGENTAPI_URL": "http://localhost:3284" + } + } + } + }' > "$config_file" + elif command_exists node; then + node -e " + const config = JSON.parse(\`$ARG_COPILOT_CONFIG\`); + config.mcpServers = { + github: { command: '@github/copilot-mcp-github' }, + coder: { + command: 'coder', + args: ['exp', 'mcp', 'server'], + type: 'stdio', + env: { + CODER_MCP_APP_STATUS_SLUG: '$ARG_MCP_APP_STATUS_SLUG', + CODER_MCP_AI_AGENTAPI_URL: 'http://localhost:3284' + } + } + }; + console.log(JSON.stringify(config, null, 2)); + " > "$config_file" + else + echo "$ARG_COPILOT_CONFIG" > "$config_file" + fi + else + echo "Adding default GitHub MCP server..." + if command_exists jq; then + echo "$ARG_COPILOT_CONFIG" | jq '. + { + mcpServers: { + "github": { + "command": "@github/copilot-mcp-github" + } + } + }' > "$config_file" + elif command_exists node; then + node -e " + const config = JSON.parse(\`$ARG_COPILOT_CONFIG\`); + config.mcpServers = { github: { command: '@github/copilot-mcp-github' } }; + console.log(JSON.stringify(config, null, 2)); + " > "$config_file" + else + echo "$ARG_COPILOT_CONFIG" > "$config_file" + fi + fi + fi else echo "ERROR: No Copilot configuration provided" exit 1 diff --git a/registry/coder-labs/modules/copilot-cli/scripts/start.sh b/registry/coder-labs/modules/copilot-cli/scripts/start.sh index eefee7288..4053dd7da 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/start.sh +++ b/registry/coder-labs/modules/copilot-cli/scripts/start.sh @@ -28,7 +28,6 @@ validate_copilot_installation() { build_copilot_args() { COPILOT_ARGS=() - # Combine system prompt with AI prompt if both exist if [ -n "$ARG_SYSTEM_PROMPT" ] && [ -n "$ARG_AI_PROMPT" ]; then local combined_prompt="$ARG_SYSTEM_PROMPT @@ -85,14 +84,12 @@ configure_copilot_model() { setup_github_authentication() { echo "Setting up GitHub authentication..." - # Check for provided token first (highest priority) if [ -n "$GITHUB_TOKEN" ]; then export GH_TOKEN="$GITHUB_TOKEN" echo "✓ Using GitHub token from module configuration" return 0 fi - # Try external auth if command_exists coder; then local github_token if github_token=$(coder external-auth access-token "${ARG_EXTERNAL_AUTH_ID:-github}" 2> /dev/null); then @@ -105,7 +102,6 @@ setup_github_authentication() { fi fi - # Try GitHub CLI as fallback if command_exists gh && gh auth status > /dev/null 2>&1; then echo "✓ Using GitHub CLI OAuth authentication" return 0 @@ -114,7 +110,7 @@ setup_github_authentication() { echo "⚠ No GitHub authentication available" echo " Copilot CLI will prompt for login during first use" echo " Use the '/login' command in Copilot CLI to authenticate" - return 0 # Don't fail - let Copilot CLI handle authentication + return 0 } start_agentapi() { @@ -123,26 +119,11 @@ start_agentapi() { build_copilot_args - local mcp_args=() - local module_path="$HOME/.copilot-module" - - if [ -f "$module_path/mcp_config.json" ]; then - mcp_args+=(--mcp-config "$module_path/mcp_config.json") - fi - if [ ${#COPILOT_ARGS[@]} -gt 0 ]; then echo "Copilot arguments: ${COPILOT_ARGS[*]}" - if [ ${#mcp_args[@]} -gt 0 ]; then - agentapi server --type claude --term-width 120 --term-height 40 "${mcp_args[@]}" -- copilot "${COPILOT_ARGS[@]}" - else - agentapi server --type claude --term-width 120 --term-height 40 -- copilot "${COPILOT_ARGS[@]}" - fi + agentapi server --type claude --term-width 120 --term-height 40 -- copilot "${COPILOT_ARGS[@]}" else - if [ ${#mcp_args[@]} -gt 0 ]; then - agentapi server --type claude --term-width 120 --term-height 40 "${mcp_args[@]}" -- copilot - else - agentapi server --type claude --term-width 120 --term-height 40 -- copilot - fi + agentapi server --type claude --term-width 120 --term-height 40 -- copilot fi } From e32b159d24c88ec40b78171c6b2785b2fe1323d0 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Tue, 30 Sep 2025 23:05:53 -0500 Subject: [PATCH 14/58] chore: bun run fmt --- registry/coder-labs/modules/copilot-cli/scripts/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/coder-labs/modules/copilot-cli/scripts/install.sh b/registry/coder-labs/modules/copilot-cli/scripts/install.sh index 20ea8fe7a..24dbce989 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/install.sh +++ b/registry/coder-labs/modules/copilot-cli/scripts/install.sh @@ -91,7 +91,7 @@ setup_copilot_config() { if [ -n "$ARG_COPILOT_CONFIG" ]; then echo "Setting up Copilot configuration with MCP servers..." - + if [ -n "$ARG_MCP_CONFIG" ]; then echo "Merging custom MCP servers into Copilot configuration..." if command_exists jq; then From 5f1a86c93db691db06462a9479b3a3bb59f31b3b Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Wed, 1 Oct 2025 07:59:09 -0500 Subject: [PATCH 15/58] feat: update configuration setup --- .../coder-labs/modules/copilot-cli/README.md | 14 +- .../coder-labs/modules/copilot-cli/main.tf | 1 - .../modules/copilot-cli/scripts/install.sh | 202 ++++++++---------- 3 files changed, 98 insertions(+), 119 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/README.md b/registry/coder-labs/modules/copilot-cli/README.md index d66cb8c38..8c8aacbfd 100644 --- a/registry/coder-labs/modules/copilot-cli/README.md +++ b/registry/coder-labs/modules/copilot-cli/README.md @@ -66,8 +66,9 @@ module "copilot_cli" { mcp_config = jsonencode({ mcpServers = { - github = { - command = "@github/copilot-mcp-github" + custom_server = { + command = "npx" + args = ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/allowed/files"] } } }) @@ -94,6 +95,15 @@ module "copilot_cli" { } ``` +## Configuration Files + +This module creates and manages configuration files in `~/.config/copilot-cli/`: + +- `config.json` - Copilot CLI settings (banner, theme, trusted directories) +- `mcp-config.json` - Model Context Protocol server definitions + +The module automatically configures GitHub and Coder MCP servers, and merges any custom MCP servers you provide via `mcp_config`. + ### Standalone Mode Run and configure Copilot CLI as a standalone tool in your workspace. diff --git a/registry/coder-labs/modules/copilot-cli/main.tf b/registry/coder-labs/modules/copilot-cli/main.tf index f4d3a7835..2f69238a6 100644 --- a/registry/coder-labs/modules/copilot-cli/main.tf +++ b/registry/coder-labs/modules/copilot-cli/main.tf @@ -164,7 +164,6 @@ variable "post_install_script" { data "coder_workspace" "me" {} data "coder_workspace_owner" "me" {} - locals { workdir = trimsuffix(var.workdir, "/") app_slug = "copilot-cli" diff --git a/registry/coder-labs/modules/copilot-cli/scripts/install.sh b/registry/coder-labs/modules/copilot-cli/scripts/install.sh index 24dbce989..4ed333956 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/install.sh +++ b/registry/coder-labs/modules/copilot-cli/scripts/install.sh @@ -87,132 +87,102 @@ setup_copilot_configurations() { } setup_copilot_config() { - local config_file="$HOME/.config/copilot.json" + local copilot_config_dir="$HOME/.config/copilot-cli" + local copilot_config_file="$copilot_config_dir/config.json" + local mcp_config_file="$copilot_config_dir/mcp-config.json" + + mkdir -p "$copilot_config_dir" if [ -n "$ARG_COPILOT_CONFIG" ]; then - echo "Setting up Copilot configuration with MCP servers..." - - if [ -n "$ARG_MCP_CONFIG" ]; then - echo "Merging custom MCP servers into Copilot configuration..." - if command_exists jq; then - local merged_config - merged_config=$(echo "$ARG_COPILOT_CONFIG" | jq --argjson mcp_config "$ARG_MCP_CONFIG" ' - . + { - mcpServers: ($mcp_config.mcpServers // {}) | - if .github == null then - .github = {"command": "@github/copilot-mcp-github"} - else . end | - if "'"$ARG_REPORT_TASKS"'" == "true" then - .coder = { - "command": "coder", - "args": ["exp", "mcp", "server"], - "type": "stdio", - "env": { - "CODER_MCP_APP_STATUS_SLUG": "'"$ARG_MCP_APP_STATUS_SLUG"'", - "CODER_MCP_AI_AGENTAPI_URL": "http://localhost:3284" - } - } - else . end - } - ') - echo "$merged_config" > "$config_file" - elif command_exists node; then - node -e " - const copilotConfig = JSON.parse(\`$ARG_COPILOT_CONFIG\`); - const mcpConfig = JSON.parse(\`$ARG_MCP_CONFIG\`); - - copilotConfig.mcpServers = mcpConfig.mcpServers || {}; - - if (!copilotConfig.mcpServers.github) { - copilotConfig.mcpServers.github = { - command: '@github/copilot-mcp-github' - }; - } - - if ('$ARG_REPORT_TASKS' === 'true') { - copilotConfig.mcpServers.coder = { - command: 'coder', - args: ['exp', 'mcp', 'server'], - type: 'stdio', - env: { - CODER_MCP_APP_STATUS_SLUG: '$ARG_MCP_APP_STATUS_SLUG', - CODER_MCP_AI_AGENTAPI_URL: 'http://localhost:3284' - } - }; - } - - console.log(JSON.stringify(copilotConfig, null, 2)); - " > "$config_file" - else - echo "$ARG_COPILOT_CONFIG" > "$config_file" - fi + echo "Setting up Copilot CLI configuration..." + + if command_exists jq; then + echo "$ARG_COPILOT_CONFIG" | jq 'del(.mcpServers)' > "$copilot_config_file" else - if [ "$ARG_REPORT_TASKS" = "true" ]; then - echo "Adding default MCP servers with Coder task reporting..." - if command_exists jq; then - echo "$ARG_COPILOT_CONFIG" | jq '. + { - mcpServers: { - "github": { - "command": "@github/copilot-mcp-github" - }, - "coder": { - "command": "coder", - "args": ["exp", "mcp", "server"], - "type": "stdio", - "env": { - "CODER_MCP_APP_STATUS_SLUG": "'"$ARG_MCP_APP_STATUS_SLUG"'", - "CODER_MCP_AI_AGENTAPI_URL": "http://localhost:3284" - } - } - } - }' > "$config_file" - elif command_exists node; then - node -e " - const config = JSON.parse(\`$ARG_COPILOT_CONFIG\`); - config.mcpServers = { - github: { command: '@github/copilot-mcp-github' }, - coder: { - command: 'coder', - args: ['exp', 'mcp', 'server'], - type: 'stdio', - env: { - CODER_MCP_APP_STATUS_SLUG: '$ARG_MCP_APP_STATUS_SLUG', - CODER_MCP_AI_AGENTAPI_URL: 'http://localhost:3284' - } - } - }; - console.log(JSON.stringify(config, null, 2)); - " > "$config_file" - else - echo "$ARG_COPILOT_CONFIG" > "$config_file" - fi - else - echo "Adding default GitHub MCP server..." - if command_exists jq; then - echo "$ARG_COPILOT_CONFIG" | jq '. + { - mcpServers: { - "github": { - "command": "@github/copilot-mcp-github" - } - } - }' > "$config_file" - elif command_exists node; then - node -e " - const config = JSON.parse(\`$ARG_COPILOT_CONFIG\`); - config.mcpServers = { github: { command: '@github/copilot-mcp-github' } }; - console.log(JSON.stringify(config, null, 2)); - " > "$config_file" - else - echo "$ARG_COPILOT_CONFIG" > "$config_file" - fi - fi + echo "$ARG_COPILOT_CONFIG" > "$copilot_config_file" fi + + echo "Setting up MCP server configuration..." + setup_mcp_config "$mcp_config_file" else echo "ERROR: No Copilot configuration provided" exit 1 fi } +setup_mcp_config() { + local mcp_config_file="$1" + + local mcp_servers="{}" + + if [ -n "$ARG_MCP_CONFIG" ]; then + echo "Adding custom MCP servers..." + if command_exists jq; then + mcp_servers=$(echo "$ARG_MCP_CONFIG" | jq '.mcpServers // {}') + else + mcp_servers="$ARG_MCP_CONFIG" + fi + fi + + if command_exists jq; then + mcp_servers=$(echo "$mcp_servers" | jq '. + { + "github": { + "command": "@github/copilot-mcp-github" + } + }') + elif command_exists node; then + mcp_servers=$(node -e " + const servers = JSON.parse(\`$mcp_servers\`); + servers.github = { command: '@github/copilot-mcp-github' }; + console.log(JSON.stringify(servers)); + ") + fi + + if [ "$ARG_REPORT_TASKS" = "true" ]; then + echo "Adding Coder MCP server for task reporting..." + if command_exists jq; then + mcp_servers=$(echo "$mcp_servers" | jq '. + { + "coder": { + "command": "coder", + "args": ["exp", "mcp", "server"], + "type": "stdio", + "env": { + "CODER_MCP_APP_STATUS_SLUG": "'"$ARG_MCP_APP_STATUS_SLUG"'", + "CODER_MCP_AI_AGENTAPI_URL": "http://localhost:3284" + } + } + }') + elif command_exists node; then + mcp_servers=$(node -e " + const servers = JSON.parse(\`$mcp_servers\`); + servers.coder = { + command: 'coder', + args: ['exp', 'mcp', 'server'], + type: 'stdio', + env: { + CODER_MCP_APP_STATUS_SLUG: '$ARG_MCP_APP_STATUS_SLUG', + CODER_MCP_AI_AGENTAPI_URL: 'http://localhost:3284' + } + }; + console.log(JSON.stringify(servers)); + ") + fi + fi + + if command_exists jq; then + echo "$mcp_servers" | jq '{mcpServers: .}' > "$mcp_config_file" + elif command_exists node; then + node -e " + const servers = JSON.parse(\`$mcp_servers\`); + console.log(JSON.stringify({mcpServers: servers}, null, 2)); + " > "$mcp_config_file" + else + echo "{\"mcpServers\": $mcp_servers}" > "$mcp_config_file" + fi + + echo "MCP configuration written to: $mcp_config_file" +} + configure_coder_integration() { if [ "$ARG_REPORT_TASKS" = "true" ]; then echo "Configuring Copilot CLI task reporting..." From a6170e439f366b433c32bdd05a26877b1f2ac747 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Wed, 1 Oct 2025 08:23:11 -0500 Subject: [PATCH 16/58] fix: update README and install script for Copilot CLI configuration paths --- .../coder-labs/modules/copilot-cli/README.md | 26 ++++++++++++++++--- .../modules/copilot-cli/scripts/install.sh | 2 +- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/README.md b/registry/coder-labs/modules/copilot-cli/README.md index 8c8aacbfd..d97884240 100644 --- a/registry/coder-labs/modules/copilot-cli/README.md +++ b/registry/coder-labs/modules/copilot-cli/README.md @@ -34,7 +34,9 @@ module "copilot_cli" { ## Examples -### Usage with Tasks and Advanced Configuration +### Usage with Tasks (Recommended for Development) + +For development environments where you want Copilot CLI to have full access to tools without prompting: ```tf data "coder_parameter" "ai_prompt" { @@ -51,8 +53,9 @@ module "copilot_cli" { agent_id = coder_agent.example.id workdir = "/home/coder/project" - ai_prompt = data.coder_parameter.ai_prompt.value - copilot_model = "claude-sonnet-4.5" + ai_prompt = data.coder_parameter.ai_prompt.value + copilot_model = "claude-sonnet-4.5" + allow_all_tools = true system_prompt = <<-EOT You are a helpful AI coding assistant working in a development environment. @@ -61,6 +64,21 @@ module "copilot_cli" { Send a task status update to notify the user that you are ready for input, and then wait for user input. EOT + trusted_directories = ["/home/coder", "/tmp"] +} +``` + +### Advanced Configuration with Specific Tool Permissions + +For more controlled environments where you want to specify exact tools: + +```tf +module "copilot_cli" { + source = "registry.coder.com/coder-labs/copilot-cli/coder" + version = "1.0.0" + agent_id = coder_agent.example.id + workdir = "/home/coder/project" + allow_tools = ["shell(git)", "shell(npm)", "write"] trusted_directories = ["/home/coder/workspace", "/tmp"] @@ -97,7 +115,7 @@ module "copilot_cli" { ## Configuration Files -This module creates and manages configuration files in `~/.config/copilot-cli/`: +This module creates and manages configuration files in `~/.copilot/`: - `config.json` - Copilot CLI settings (banner, theme, trusted directories) - `mcp-config.json` - Model Context Protocol server definitions diff --git a/registry/coder-labs/modules/copilot-cli/scripts/install.sh b/registry/coder-labs/modules/copilot-cli/scripts/install.sh index 4ed333956..b33bb9f68 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/install.sh +++ b/registry/coder-labs/modules/copilot-cli/scripts/install.sh @@ -87,7 +87,7 @@ setup_copilot_configurations() { } setup_copilot_config() { - local copilot_config_dir="$HOME/.config/copilot-cli" + local copilot_config_dir="$HOME/.copilot" local copilot_config_file="$copilot_config_dir/config.json" local mcp_config_file="$copilot_config_dir/mcp-config.json" From 4f5636d2652b17183258ad54d6c0ad72a611a5eb Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Wed, 1 Oct 2025 08:36:26 -0500 Subject: [PATCH 17/58] feat: correct MCP configuration with required parameters for GitHub and Coder integrations --- .../modules/copilot-cli/scripts/install.sh | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/scripts/install.sh b/registry/coder-labs/modules/copilot-cli/scripts/install.sh index b33bb9f68..352cab460 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/install.sh +++ b/registry/coder-labs/modules/copilot-cli/scripts/install.sh @@ -127,13 +127,21 @@ setup_mcp_config() { if command_exists jq; then mcp_servers=$(echo "$mcp_servers" | jq '. + { "github": { - "command": "@github/copilot-mcp-github" + "command": "@github/copilot-mcp-github", + "args": [], + "type": "local", + "tools": [] } }') elif command_exists node; then mcp_servers=$(node -e " const servers = JSON.parse(\`$mcp_servers\`); - servers.github = { command: '@github/copilot-mcp-github' }; + servers.github = { + command: '@github/copilot-mcp-github', + args: [], + type: 'local', + tools: [] + }; console.log(JSON.stringify(servers)); ") fi @@ -145,7 +153,8 @@ setup_mcp_config() { "coder": { "command": "coder", "args": ["exp", "mcp", "server"], - "type": "stdio", + "type": "local", + "tools": [], "env": { "CODER_MCP_APP_STATUS_SLUG": "'"$ARG_MCP_APP_STATUS_SLUG"'", "CODER_MCP_AI_AGENTAPI_URL": "http://localhost:3284" @@ -158,7 +167,8 @@ setup_mcp_config() { servers.coder = { command: 'coder', args: ['exp', 'mcp', 'server'], - type: 'stdio', + type: 'local', + tools: [], env: { CODER_MCP_APP_STATUS_SLUG: '$ARG_MCP_APP_STATUS_SLUG', CODER_MCP_AI_AGENTAPI_URL: 'http://localhost:3284' From 9e3c3ae380ff4dc66f9aa6d9f2e3f5916a948722 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Wed, 1 Oct 2025 09:03:24 -0500 Subject: [PATCH 18/58] fix: simplify MCP server configuration --- .../modules/copilot-cli/scripts/install.sh | 68 ++++--------------- 1 file changed, 12 insertions(+), 56 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/scripts/install.sh b/registry/coder-labs/modules/copilot-cli/scripts/install.sh index 352cab460..8906717eb 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/install.sh +++ b/registry/coder-labs/modules/copilot-cli/scripts/install.sh @@ -124,61 +124,6 @@ setup_mcp_config() { fi fi - if command_exists jq; then - mcp_servers=$(echo "$mcp_servers" | jq '. + { - "github": { - "command": "@github/copilot-mcp-github", - "args": [], - "type": "local", - "tools": [] - } - }') - elif command_exists node; then - mcp_servers=$(node -e " - const servers = JSON.parse(\`$mcp_servers\`); - servers.github = { - command: '@github/copilot-mcp-github', - args: [], - type: 'local', - tools: [] - }; - console.log(JSON.stringify(servers)); - ") - fi - - if [ "$ARG_REPORT_TASKS" = "true" ]; then - echo "Adding Coder MCP server for task reporting..." - if command_exists jq; then - mcp_servers=$(echo "$mcp_servers" | jq '. + { - "coder": { - "command": "coder", - "args": ["exp", "mcp", "server"], - "type": "local", - "tools": [], - "env": { - "CODER_MCP_APP_STATUS_SLUG": "'"$ARG_MCP_APP_STATUS_SLUG"'", - "CODER_MCP_AI_AGENTAPI_URL": "http://localhost:3284" - } - } - }') - elif command_exists node; then - mcp_servers=$(node -e " - const servers = JSON.parse(\`$mcp_servers\`); - servers.coder = { - command: 'coder', - args: ['exp', 'mcp', 'server'], - type: 'local', - tools: [], - env: { - CODER_MCP_APP_STATUS_SLUG: '$ARG_MCP_APP_STATUS_SLUG', - CODER_MCP_AI_AGENTAPI_URL: 'http://localhost:3284' - } - }; - console.log(JSON.stringify(servers)); - ") - fi - fi - if command_exists jq; then echo "$mcp_servers" | jq '{mcpServers: .}' > "$mcp_config_file" elif command_exists node; then @@ -190,7 +135,11 @@ setup_mcp_config() { echo "{\"mcpServers\": $mcp_servers}" > "$mcp_config_file" fi - echo "MCP configuration written to: $mcp_config_file" + if [ -n "$ARG_MCP_CONFIG" ]; then + echo "Custom MCP configuration written to: $mcp_config_file" + else + echo "Empty MCP configuration file created at: $mcp_config_file" + fi } configure_coder_integration() { @@ -200,10 +149,17 @@ configure_coder_integration() { export CODER_MCP_AI_AGENTAPI_URL="http://localhost:3284" if command_exists coder; then + echo "Setting up Coder MCP integration for Copilot CLI..." coder exp mcp configure copilot-cli "$ARG_WORKDIR" 2> /dev/null || true fi else echo "Task reporting disabled." + if command_exists coder; then + export CODER_MCP_APP_STATUS_SLUG="" + export CODER_MCP_AI_AGENTAPI_URL="" + echo "Configuring Copilot CLI with Coder MCP (no task reporting)..." + coder exp mcp configure copilot-cli "$ARG_WORKDIR" 2> /dev/null || true + fi fi } From 8cd93ad374c4afc8e3867a1cb1cce80d3fa9c454 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Wed, 1 Oct 2025 09:25:37 -0500 Subject: [PATCH 19/58] fix: move mcp server creation back to manual method --- .../coder-labs/modules/copilot-cli/main.tf | 3 - .../modules/copilot-cli/scripts/install.sh | 78 ++++++++++++++----- 2 files changed, 58 insertions(+), 23 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/main.tf b/registry/coder-labs/modules/copilot-cli/main.tf index 2f69238a6..ceba4e892 100644 --- a/registry/coder-labs/modules/copilot-cli/main.tf +++ b/registry/coder-labs/modules/copilot-cli/main.tf @@ -171,7 +171,6 @@ locals { start_script = file("${path.module}/scripts/start.sh") module_dir_name = ".copilot-module" - # Default configuration with workdir as trusted folder default_copilot_config = jsonencode({ banner = "never" theme = "auto" @@ -201,8 +200,6 @@ resource "coder_env" "github_token" { value = var.github_token } - - module "agentapi" { source = "registry.coder.com/coder/agentapi/coder" version = "1.1.1" diff --git a/registry/coder-labs/modules/copilot-cli/scripts/install.sh b/registry/coder-labs/modules/copilot-cli/scripts/install.sh index 8906717eb..ef790c60d 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/install.sh +++ b/registry/coder-labs/modules/copilot-cli/scripts/install.sh @@ -113,17 +113,67 @@ setup_copilot_config() { setup_mcp_config() { local mcp_config_file="$1" + # Start with Coder MCP server if task reporting is enabled local mcp_servers="{}" + if [ "$ARG_REPORT_TASKS" = "true" ] && [ -n "$ARG_MCP_APP_STATUS_SLUG" ]; then + echo "Adding Coder MCP server for task reporting..." + + # Create wrapper script for Coder MCP server + cat << EOF > /tmp/copilot-mcp-wrapper.sh +#!/usr/bin/env bash +set -e + +export CODER_MCP_APP_STATUS_SLUG="${ARG_MCP_APP_STATUS_SLUG}" +export CODER_MCP_AI_AGENTAPI_URL="http://localhost:3284" +export CODER_AGENT_URL="${CODER_AGENT_URL}" +export CODER_AGENT_TOKEN="${CODER_AGENT_TOKEN}" + +exec coder exp mcp server +EOF + chmod +x /tmp/copilot-mcp-wrapper.sh + + # Define Coder MCP server configuration + mcp_servers=$( + cat << 'EOF' +{ + "coder": { + "command": "/tmp/copilot-mcp-wrapper.sh", + "args": [], + "description": "Report ALL tasks and statuses (in progress, done, failed) you are working on.", + "type": "stdio", + "timeout": 3000, + "trust": true + } +} +EOF + ) + fi + + # Add custom MCP servers if provided if [ -n "$ARG_MCP_CONFIG" ]; then echo "Adding custom MCP servers..." + local custom_servers if command_exists jq; then - mcp_servers=$(echo "$ARG_MCP_CONFIG" | jq '.mcpServers // {}') + custom_servers=$(echo "$ARG_MCP_CONFIG" | jq '.mcpServers // {}') + # Merge custom servers with Coder server + mcp_servers=$(echo "$mcp_servers" | jq --argjson custom "$custom_servers" '. + $custom') + elif command_exists node; then + custom_servers=$(echo "$ARG_MCP_CONFIG" | node -e " + const input = JSON.parse(require('fs').readFileSync(0, 'utf8')); + console.log(JSON.stringify(input.mcpServers || {})); + ") + mcp_servers=$(node -e " + const existing = JSON.parse(\`$mcp_servers\`); + const custom = JSON.parse(\`$custom_servers\`); + console.log(JSON.stringify({...existing, ...custom})); + ") else - mcp_servers="$ARG_MCP_CONFIG" + echo "WARNING: jq and node not available, cannot merge custom MCP servers" fi fi + # Write final MCP configuration if command_exists jq; then echo "$mcp_servers" | jq '{mcpServers: .}' > "$mcp_config_file" elif command_exists node; then @@ -135,31 +185,19 @@ setup_mcp_config() { echo "{\"mcpServers\": $mcp_servers}" > "$mcp_config_file" fi - if [ -n "$ARG_MCP_CONFIG" ]; then - echo "Custom MCP configuration written to: $mcp_config_file" - else - echo "Empty MCP configuration file created at: $mcp_config_file" - fi + echo "MCP configuration written to: $mcp_config_file" } configure_coder_integration() { - if [ "$ARG_REPORT_TASKS" = "true" ]; then + if [ "$ARG_REPORT_TASKS" = "true" ] && [ -n "$ARG_MCP_APP_STATUS_SLUG" ]; then echo "Configuring Copilot CLI task reporting..." export CODER_MCP_APP_STATUS_SLUG="$ARG_MCP_APP_STATUS_SLUG" export CODER_MCP_AI_AGENTAPI_URL="http://localhost:3284" - - if command_exists coder; then - echo "Setting up Coder MCP integration for Copilot CLI..." - coder exp mcp configure copilot-cli "$ARG_WORKDIR" 2> /dev/null || true - fi + echo "✓ Coder MCP server configured for task reporting" else - echo "Task reporting disabled." - if command_exists coder; then - export CODER_MCP_APP_STATUS_SLUG="" - export CODER_MCP_AI_AGENTAPI_URL="" - echo "Configuring Copilot CLI with Coder MCP (no task reporting)..." - coder exp mcp configure copilot-cli "$ARG_WORKDIR" 2> /dev/null || true - fi + echo "Task reporting disabled or no app status slug provided." + export CODER_MCP_APP_STATUS_SLUG="" + export CODER_MCP_AI_AGENTAPI_URL="" fi } From d368522fdbefe1581a1769631e12a65292ddd43d Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Wed, 1 Oct 2025 09:54:47 -0500 Subject: [PATCH 20/58] feat: simplify mcp configuration based on github help docs --- .../modules/copilot-cli/scripts/install.sh | 102 ++++++++---------- 1 file changed, 47 insertions(+), 55 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/scripts/install.sh b/registry/coder-labs/modules/copilot-cli/scripts/install.sh index ef790c60d..8a58b83e3 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/install.sh +++ b/registry/coder-labs/modules/copilot-cli/scripts/install.sh @@ -113,79 +113,71 @@ setup_copilot_config() { setup_mcp_config() { local mcp_config_file="$1" - # Start with Coder MCP server if task reporting is enabled - local mcp_servers="{}" + echo '{"mcpServers": {}}' > "$mcp_config_file" if [ "$ARG_REPORT_TASKS" = "true" ] && [ -n "$ARG_MCP_APP_STATUS_SLUG" ]; then echo "Adding Coder MCP server for task reporting..." + setup_coder_mcp_server "$mcp_config_file" + fi - # Create wrapper script for Coder MCP server - cat << EOF > /tmp/copilot-mcp-wrapper.sh -#!/usr/bin/env bash -set -e + if [ -n "$ARG_MCP_CONFIG" ]; then + echo "Adding custom MCP servers..." + add_custom_mcp_servers "$mcp_config_file" + fi -export CODER_MCP_APP_STATUS_SLUG="${ARG_MCP_APP_STATUS_SLUG}" -export CODER_MCP_AI_AGENTAPI_URL="http://localhost:3284" -export CODER_AGENT_URL="${CODER_AGENT_URL}" -export CODER_AGENT_TOKEN="${CODER_AGENT_TOKEN}" + echo "MCP configuration completed: $mcp_config_file" +} -exec coder exp mcp server -EOF - chmod +x /tmp/copilot-mcp-wrapper.sh +setup_coder_mcp_server() { + local mcp_config_file="$1" - # Define Coder MCP server configuration - mcp_servers=$( - cat << 'EOF' + local coder_mcp_config + coder_mcp_config=$( + cat << EOF { - "coder": { - "command": "/tmp/copilot-mcp-wrapper.sh", - "args": [], - "description": "Report ALL tasks and statuses (in progress, done, failed) you are working on.", - "type": "stdio", - "timeout": 3000, - "trust": true + "mcpServers": { + "coder": { + "type": "local", + "command": "coder", + "args": ["exp", "mcp", "server"], + "tools": ["*"], + "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 - ) - fi + ) - # Add custom MCP servers if provided - if [ -n "$ARG_MCP_CONFIG" ]; then - echo "Adding custom MCP servers..." - local custom_servers - if command_exists jq; then - custom_servers=$(echo "$ARG_MCP_CONFIG" | jq '.mcpServers // {}') - # Merge custom servers with Coder server - mcp_servers=$(echo "$mcp_servers" | jq --argjson custom "$custom_servers" '. + $custom') - elif command_exists node; then - custom_servers=$(echo "$ARG_MCP_CONFIG" | node -e " - const input = JSON.parse(require('fs').readFileSync(0, 'utf8')); - console.log(JSON.stringify(input.mcpServers || {})); - ") - mcp_servers=$(node -e " - const existing = JSON.parse(\`$mcp_servers\`); - const custom = JSON.parse(\`$custom_servers\`); - console.log(JSON.stringify({...existing, ...custom})); - ") - else - echo "WARNING: jq and node not available, cannot merge custom MCP servers" - fi - fi + echo "$coder_mcp_config" > "$mcp_config_file" +} + +add_custom_mcp_servers() { + local mcp_config_file="$1" - # Write final MCP configuration if command_exists jq; then - echo "$mcp_servers" | jq '{mcpServers: .}' > "$mcp_config_file" + local custom_servers + custom_servers=$(echo "$ARG_MCP_CONFIG" | jq '.mcpServers // {}') + + local updated_config + updated_config=$(jq --argjson custom "$custom_servers" '.mcpServers += $custom' "$mcp_config_file") + echo "$updated_config" > "$mcp_config_file" elif command_exists node; then node -e " - const servers = JSON.parse(\`$mcp_servers\`); - console.log(JSON.stringify({mcpServers: servers}, null, 2)); - " > "$mcp_config_file" + const fs = require('fs'); + const existing = JSON.parse(fs.readFileSync('$mcp_config_file', 'utf8')); + const input = JSON.parse(\`$ARG_MCP_CONFIG\`); + const custom = input.mcpServers || {}; + existing.mcpServers = {...existing.mcpServers, ...custom}; + fs.writeFileSync('$mcp_config_file', JSON.stringify(existing, null, 2)); + " else - echo "{\"mcpServers\": $mcp_servers}" > "$mcp_config_file" + echo "WARNING: jq and node not available, cannot merge custom MCP servers" fi - - echo "MCP configuration written to: $mcp_config_file" } configure_coder_integration() { From f08471f157229edada3b524691c68902ff0556a0 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Wed, 1 Oct 2025 10:20:42 -0500 Subject: [PATCH 21/58] feat: use hack method for coder mcp server --- .../coder-labs/modules/copilot-cli/README.md | 13 +++++-- .../modules/copilot-cli/main.test.ts | 2 -- .../modules/copilot-cli/scripts/install.sh | 36 +++++++++++++------ 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/README.md b/registry/coder-labs/modules/copilot-cli/README.md index d97884240..f397507e9 100644 --- a/registry/coder-labs/modules/copilot-cli/README.md +++ b/registry/coder-labs/modules/copilot-cli/README.md @@ -207,9 +207,10 @@ This module works with multiple GitHub authentication methods in priority order: ## Task Reporting -When `report_tasks = true` (default), this module automatically configures the **Coder MCP server** for task reporting integration: +When `report_tasks = true` (default), this module automatically configures and starts the **Coder MCP server** for task reporting integration: + +- **Automatic Configuration**: The Coder MCP server is added to the MCP configuration automatically -- **Automatic Integration**: The Coder MCP server is added to the MCP configuration automatically - **Task Status Updates**: Copilot CLI can report task progress to the Coder UI - **No Manual Setup**: Works out-of-the-box with Coder's task reporting system - **Custom MCP Compatible**: If you provide custom `mcp_config`, the Coder MCP server is added alongside your custom servers @@ -222,6 +223,14 @@ The Coder MCP server enables Copilot CLI to: To disable task reporting, set `report_tasks = false`. +### MCP Server Configuration + +This module automatically configures MCP servers by: + +1. **Writing MCP Configuration**: Creates `~/.copilot/mcp-config.json` with the correct format +2. **Wrapper Script**: Creates `/tmp/coder-mcp-server.sh` that sets environment variables and runs `coder exp mcp server` +3. **Merging Custom Servers**: Adds any custom MCP servers you provide via the `mcp_config` variable + ## Troubleshooting If you encounter any issues, check the log files in the `~/.copilot-module` directory within your workspace for detailed information. diff --git a/registry/coder-labs/modules/copilot-cli/main.test.ts b/registry/coder-labs/modules/copilot-cli/main.test.ts index 645f4d179..e9929da59 100644 --- a/registry/coder-labs/modules/copilot-cli/main.test.ts +++ b/registry/coder-labs/modules/copilot-cli/main.test.ts @@ -271,8 +271,6 @@ describe("copilot-cli module", async () => { ); expect(statusSlugEnv).toBeDefined(); - // System prompt is now handled via .github/copilot-instructions.md file creation - // during install, not via environment variables }); it("works with full configuration", async () => { diff --git a/registry/coder-labs/modules/copilot-cli/scripts/install.sh b/registry/coder-labs/modules/copilot-cli/scripts/install.sh index 8a58b83e3..838923b42 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/install.sh +++ b/registry/coder-labs/modules/copilot-cli/scripts/install.sh @@ -131,22 +131,38 @@ setup_mcp_config() { setup_coder_mcp_server() { local mcp_config_file="$1" + local coder_mcp_wrapper_script + coder_mcp_wrapper_script=$( + cat << EOF +#!/usr/bin/env bash +set -e + +# --- Set environment variables --- +export CODER_MCP_APP_STATUS_SLUG="${ARG_MCP_APP_STATUS_SLUG}" +export CODER_MCP_AI_AGENTAPI_URL="http://localhost:3284" +export CODER_AGENT_URL="\${CODER_AGENT_URL}" +export CODER_AGENT_TOKEN="\${CODER_AGENT_TOKEN}" + +# --- Launch the MCP server --- +exec coder exp mcp server +EOF + ) + echo "$coder_mcp_wrapper_script" > "/tmp/coder-mcp-server.sh" + chmod +x /tmp/coder-mcp-server.sh + local coder_mcp_config coder_mcp_config=$( cat << EOF { "mcpServers": { "coder": { - "type": "local", - "command": "coder", - "args": ["exp", "mcp", "server"], - "tools": ["*"], - "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}" - } + "command": "/tmp/coder-mcp-server.sh", + "args": [], + "description": "Report ALL tasks and statuses (in progress, done, failed) you are working on.", + "name": "Coder", + "timeout": 3000, + "type": "stdio", + "trust": true } } } From d460bbb81fc9ee85b89ad8832ce5755193f34882 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Wed, 1 Oct 2025 10:21:29 -0500 Subject: [PATCH 22/58] chore: bun run fmt --- registry/coder-labs/modules/copilot-cli/main.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/registry/coder-labs/modules/copilot-cli/main.test.ts b/registry/coder-labs/modules/copilot-cli/main.test.ts index e9929da59..d1095a67e 100644 --- a/registry/coder-labs/modules/copilot-cli/main.test.ts +++ b/registry/coder-labs/modules/copilot-cli/main.test.ts @@ -270,7 +270,6 @@ describe("copilot-cli module", async () => { "mcp_app_status_slug", ); expect(statusSlugEnv).toBeDefined(); - }); it("works with full configuration", async () => { From dd970658de2614e4ca42383acaa1e27a36e7bb27 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Wed, 1 Oct 2025 10:34:26 -0500 Subject: [PATCH 23/58] fix: update MCP server configuration to use local type and allow all tools --- registry/coder-labs/modules/copilot-cli/scripts/install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/registry/coder-labs/modules/copilot-cli/scripts/install.sh b/registry/coder-labs/modules/copilot-cli/scripts/install.sh index 838923b42..b665df95e 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/install.sh +++ b/registry/coder-labs/modules/copilot-cli/scripts/install.sh @@ -161,7 +161,8 @@ EOF "description": "Report ALL tasks and statuses (in progress, done, failed) you are working on.", "name": "Coder", "timeout": 3000, - "type": "stdio", + "type": "local", + "tools": ["*"], "trust": true } } From 8151766f1e6f81aea2ecf2a445462632ef4293f1 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Wed, 1 Oct 2025 11:01:13 -0500 Subject: [PATCH 24/58] fix: update AgentAPI version to v0.8.0 and correct environment variable exports in install script --- registry/coder-labs/modules/copilot-cli/main.tf | 2 +- registry/coder-labs/modules/copilot-cli/scripts/install.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/main.tf b/registry/coder-labs/modules/copilot-cli/main.tf index ceba4e892..bd23cb895 100644 --- a/registry/coder-labs/modules/copilot-cli/main.tf +++ b/registry/coder-labs/modules/copilot-cli/main.tf @@ -98,7 +98,7 @@ variable "install_agentapi" { variable "agentapi_version" { type = string description = "The version of AgentAPI to install." - default = "v0.7.1" + default = "v0.8.0" } variable "report_tasks" { diff --git a/registry/coder-labs/modules/copilot-cli/scripts/install.sh b/registry/coder-labs/modules/copilot-cli/scripts/install.sh index b665df95e..427f08c91 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/install.sh +++ b/registry/coder-labs/modules/copilot-cli/scripts/install.sh @@ -140,8 +140,8 @@ set -e # --- Set environment variables --- export CODER_MCP_APP_STATUS_SLUG="${ARG_MCP_APP_STATUS_SLUG}" export CODER_MCP_AI_AGENTAPI_URL="http://localhost:3284" -export CODER_AGENT_URL="\${CODER_AGENT_URL}" -export CODER_AGENT_TOKEN="\${CODER_AGENT_TOKEN}" +export CODER_AGENT_URL="${CODER_AGENT_URL}" +export CODER_AGENT_TOKEN="${CODER_AGENT_TOKEN}" # --- Launch the MCP server --- exec coder exp mcp server From 868b0a6486edb885616695aa3ba0d6040362a136 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Wed, 1 Oct 2025 12:04:44 -0500 Subject: [PATCH 25/58] feat: add session resumption support for Copilot CLI and update related configurations --- .../coder-labs/modules/copilot-cli/README.md | 25 +++++++++- .../modules/copilot-cli/main.test.ts | 16 ++++++ .../coder-labs/modules/copilot-cli/main.tf | 7 +++ .../modules/copilot-cli/scripts/install.sh | 13 ++++- .../modules/copilot-cli/scripts/start.sh | 49 +++++++++---------- 5 files changed, 80 insertions(+), 30 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/README.md b/registry/coder-labs/modules/copilot-cli/README.md index f397507e9..d138d5cbb 100644 --- a/registry/coder-labs/modules/copilot-cli/README.md +++ b/registry/coder-labs/modules/copilot-cli/README.md @@ -34,9 +34,9 @@ module "copilot_cli" { ## Examples -### Usage with Tasks (Recommended for Development) +### Usage with Tasks -For development environments where you want Copilot CLI to have full access to tools without prompting: +For development environments where you want Copilot CLI to have full access to tools and automatically resume sessions: ```tf data "coder_parameter" "ai_prompt" { @@ -56,6 +56,7 @@ module "copilot_cli" { ai_prompt = data.coder_parameter.ai_prompt.value copilot_model = "claude-sonnet-4.5" allow_all_tools = true + resume_session = true system_prompt = <<-EOT You are a helpful AI coding assistant working in a development environment. @@ -205,6 +206,26 @@ This module works with multiple GitHub authentication methods in priority order: > **Note**: OAuth tokens work best with Copilot CLI. Personal Access Tokens may have limited functionality. +## Session Resumption + +By default (`resume_session = true`), this module automatically resumes the latest Copilot CLI session when the workspace is restarted. This provides a seamless experience where: + +- **Previous conversations continue** - No need to re-establish context +- **No duplicate prompts** - Initial prompts are only sent on first workspace creation +- **Workspace restart handling** - Automatically detects and resumes existing sessions + +```tf +module "copilot_cli" { + source = "registry.coder.com/coder-labs/copilot-cli/coder" + version = "1.0.0" + agent_id = coder_agent.example.id + workdir = "/home/coder/project" + + resume_session = true # Default: automatically resume sessions + # resume_session = false # Always start fresh sessions +} +``` + ## Task Reporting When `report_tasks = true` (default), this module automatically configures and starts the **Coder MCP server** for task reporting integration: diff --git a/registry/coder-labs/modules/copilot-cli/main.test.ts b/registry/coder-labs/modules/copilot-cli/main.test.ts index d1095a67e..46dbfe0f9 100644 --- a/registry/coder-labs/modules/copilot-cli/main.test.ts +++ b/registry/coder-labs/modules/copilot-cli/main.test.ts @@ -309,4 +309,20 @@ describe("copilot-cli module", async () => { expect(githubTokenEnv.name).toBe("GITHUB_TOKEN"); expect(githubTokenEnv.value).toBe("test_github_token_123"); }); + + it("supports resume session configuration", async () => { + const resumeSessionVars = { + ...requiredVars, + resume_session: false, + }; + + const state = await runTerraformApply(moduleDir, resumeSessionVars); + + const statusSlugEnv = findResourceInstance( + state, + "coder_env", + "mcp_app_status_slug", + ); + expect(statusSlugEnv).toBeDefined(); + }); }); diff --git a/registry/coder-labs/modules/copilot-cli/main.tf b/registry/coder-labs/modules/copilot-cli/main.tf index bd23cb895..d46a04a64 100644 --- a/registry/coder-labs/modules/copilot-cli/main.tf +++ b/registry/coder-labs/modules/copilot-cli/main.tf @@ -149,6 +149,12 @@ variable "cli_app_display_name" { default = "Copilot CLI" } +variable "resume_session" { + type = bool + description = "Whether to automatically resume the latest Copilot CLI session on workspace restart." + default = true +} + variable "pre_install_script" { type = string description = "Custom script to run before configuring Copilot CLI." @@ -237,6 +243,7 @@ module "agentapi" { ARG_DENY_TOOLS='${join(",", var.deny_tools)}' \ ARG_TRUSTED_DIRECTORIES='${join(",", var.trusted_directories)}' \ ARG_EXTERNAL_AUTH_ID='${var.external_auth_id}' \ + ARG_RESUME_SESSION='${var.resume_session}' \ /tmp/start.sh EOT diff --git a/registry/coder-labs/modules/copilot-cli/scripts/install.sh b/registry/coder-labs/modules/copilot-cli/scripts/install.sh index 427f08c91..f88f1a81a 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/install.sh +++ b/registry/coder-labs/modules/copilot-cli/scripts/install.sh @@ -137,13 +137,11 @@ setup_coder_mcp_server() { #!/usr/bin/env bash set -e -# --- Set environment variables --- export CODER_MCP_APP_STATUS_SLUG="${ARG_MCP_APP_STATUS_SLUG}" export CODER_MCP_AI_AGENTAPI_URL="http://localhost:3284" export CODER_AGENT_URL="${CODER_AGENT_URL}" export CODER_AGENT_TOKEN="${CODER_AGENT_TOKEN}" -# --- Launch the MCP server --- exec coder exp mcp server EOF ) @@ -197,6 +195,16 @@ add_custom_mcp_servers() { fi } +configure_copilot_model() { + if [ -n "$ARG_COPILOT_MODEL" ] && [ "$ARG_COPILOT_MODEL" != "claude-sonnet-4" ]; then + echo "Setting Copilot CLI model to: $ARG_COPILOT_MODEL" + copilot config model "$ARG_COPILOT_MODEL" || { + echo "WARNING: Failed to set model via copilot config, will use environment variable fallback" + export COPILOT_MODEL="$ARG_COPILOT_MODEL" + } + fi +} + configure_coder_integration() { if [ "$ARG_REPORT_TASKS" = "true" ] && [ -n "$ARG_MCP_APP_STATUS_SLUG" ]; then echo "Configuring Copilot CLI task reporting..." @@ -214,6 +222,7 @@ validate_prerequisites install_copilot_cli check_github_authentication setup_copilot_configurations +configure_copilot_model configure_coder_integration echo "Copilot CLI module setup completed." diff --git a/registry/coder-labs/modules/copilot-cli/scripts/start.sh b/registry/coder-labs/modules/copilot-cli/scripts/start.sh index 4053dd7da..4ea487c42 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/start.sh +++ b/registry/coder-labs/modules/copilot-cli/scripts/start.sh @@ -17,6 +17,7 @@ ARG_ALLOW_TOOLS=${ARG_ALLOW_TOOLS:-} ARG_DENY_TOOLS=${ARG_DENY_TOOLS:-} ARG_TRUSTED_DIRECTORIES=${ARG_TRUSTED_DIRECTORIES:-} ARG_EXTERNAL_AUTH_ID=${ARG_EXTERNAL_AUTH_ID:-github} +ARG_RESUME_SESSION=${ARG_RESUME_SESSION:-true} validate_copilot_installation() { if ! command_exists copilot; then @@ -62,23 +63,17 @@ Task: $ARG_AI_PROMPT" fi } -configure_copilot_model() { - if [ -n "$ARG_COPILOT_MODEL" ]; then - case "$ARG_COPILOT_MODEL" in - "gpt-5") - export COPILOT_MODEL="gpt-5" - ;; - "claude-sonnet-4") - export COPILOT_MODEL="claude-sonnet-4" - ;; - "claude-sonnet-4.5") - export COPILOT_MODEL="claude-sonnet-4.5" - ;; - *) - echo "WARNING: Unknown model '$ARG_COPILOT_MODEL'. Using default." - ;; - esac +check_existing_session() { + if [ "$ARG_RESUME_SESSION" = "true" ]; then + if copilot --help > /dev/null 2>&1; then + local session_dir="$HOME/.copilot/sessions" + if [ -d "$session_dir" ] && [ "$(ls -A "$session_dir" 2> /dev/null)" ]; then + echo "Found existing Copilot CLI sessions. Resume mode enabled." + return 0 + fi + fi fi + return 1 } setup_github_authentication() { @@ -117,20 +112,22 @@ start_agentapi() { echo "Starting in directory: $ARG_WORKDIR" cd "$ARG_WORKDIR" - build_copilot_args - - if [ ${#COPILOT_ARGS[@]} -gt 0 ]; then - echo "Copilot arguments: ${COPILOT_ARGS[*]}" - agentapi server --type claude --term-width 120 --term-height 40 -- copilot "${COPILOT_ARGS[@]}" + if check_existing_session; then + echo "Resuming latest Copilot CLI session..." + agentapi server --type claude --term-width 120 --term-height 40 -- copilot --resume else - agentapi server --type claude --term-width 120 --term-height 40 -- copilot + echo "Starting new Copilot CLI session..." + build_copilot_args + + if [ ${#COPILOT_ARGS[@]} -gt 0 ]; then + echo "Copilot arguments: ${COPILOT_ARGS[*]}" + agentapi server --type claude --term-width 120 --term-height 40 -- copilot "${COPILOT_ARGS[@]}" + else + agentapi server --type claude --term-width 120 --term-height 40 -- copilot + fi fi } -configure_copilot_model - -echo "COPILOT_MODEL=${ARG_COPILOT_MODEL:-${COPILOT_MODEL:-not set}}" - setup_github_authentication validate_copilot_installation start_agentapi From 994d95183397a6125e4e949446796c26ab418350 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Wed, 1 Oct 2025 12:34:55 -0500 Subject: [PATCH 26/58] fix: update session directory path for existing Copilot CLI session detection --- registry/coder-labs/modules/copilot-cli/scripts/start.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/scripts/start.sh b/registry/coder-labs/modules/copilot-cli/scripts/start.sh index 4ea487c42..50cec5f01 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/start.sh +++ b/registry/coder-labs/modules/copilot-cli/scripts/start.sh @@ -66,8 +66,8 @@ Task: $ARG_AI_PROMPT" check_existing_session() { if [ "$ARG_RESUME_SESSION" = "true" ]; then if copilot --help > /dev/null 2>&1; then - local session_dir="$HOME/.copilot/sessions" - if [ -d "$session_dir" ] && [ "$(ls -A "$session_dir" 2> /dev/null)" ]; then + local session_dir="$HOME/.copilot/history-session-state" + if [ -d "$session_dir" ] && [ -n "$(ls "$session_dir"/session_*_*.json 2> /dev/null)" ]; then echo "Found existing Copilot CLI sessions. Resume mode enabled." return 0 fi From 3c16c5c68f653ad5f91915d089fe90caa7de5f39 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Thu, 2 Oct 2025 08:42:38 -0500 Subject: [PATCH 27/58] feat: update for new agentapi initial prompt, and type --- .../modules/copilot-cli/scripts/start.sh | 61 +++++++++++++------ 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/scripts/start.sh b/registry/coder-labs/modules/copilot-cli/scripts/start.sh index 50cec5f01..54f29fb32 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/start.sh +++ b/registry/coder-labs/modules/copilot-cli/scripts/start.sh @@ -26,20 +26,27 @@ validate_copilot_installation() { 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" +} + build_copilot_args() { COPILOT_ARGS=() - if [ -n "$ARG_SYSTEM_PROMPT" ] && [ -n "$ARG_AI_PROMPT" ]; then - local combined_prompt="$ARG_SYSTEM_PROMPT - -Task: $ARG_AI_PROMPT" - COPILOT_ARGS+=(--prompt "$combined_prompt") - elif [ -n "$ARG_SYSTEM_PROMPT" ]; then - COPILOT_ARGS+=(--prompt "$ARG_SYSTEM_PROMPT") - elif [ -n "$ARG_AI_PROMPT" ]; then - COPILOT_ARGS+=(--prompt "$ARG_AI_PROMPT") - fi - if [ "$ARG_ALLOW_ALL_TOOLS" = "true" ]; then COPILOT_ARGS+=(--allow-all-tools) fi @@ -112,18 +119,36 @@ start_agentapi() { echo "Starting in directory: $ARG_WORKDIR" cd "$ARG_WORKDIR" + build_copilot_args + if check_existing_session; then echo "Resuming latest Copilot CLI session..." - agentapi server --type claude --term-width 120 --term-height 40 -- copilot --resume - else - echo "Starting new Copilot CLI session..." - build_copilot_args - if [ ${#COPILOT_ARGS[@]} -gt 0 ]; then echo "Copilot arguments: ${COPILOT_ARGS[*]}" - agentapi server --type claude --term-width 120 --term-height 40 -- copilot "${COPILOT_ARGS[@]}" + agentapi server --type copilot --term-width 120 --term-height 40 -- copilot --resume "${COPILOT_ARGS[@]}" else - agentapi server --type claude --term-width 120 --term-height 40 -- copilot + agentapi server --type copilot --term-width 120 --term-height 40 -- copilot --resume + fi + else + echo "Starting new Copilot CLI session..." + local initial_prompt + initial_prompt=$(build_initial_prompt) + + if [ -n "$initial_prompt" ]; then + echo "Using initial prompt with system context" + if [ ${#COPILOT_ARGS[@]} -gt 0 ]; then + echo "Copilot arguments: ${COPILOT_ARGS[*]}" + agentapi server -I="$initial_prompt" --type copilot --term-width 120 --term-height 40 -- copilot "${COPILOT_ARGS[@]}" + else + agentapi server -I="$initial_prompt" --type copilot --term-width 120 --term-height 40 -- copilot + fi + else + if [ ${#COPILOT_ARGS[@]} -gt 0 ]; then + echo "Copilot arguments: ${COPILOT_ARGS[*]}" + agentapi server --type copilot --term-width 120 --term-height 40 -- copilot "${COPILOT_ARGS[@]}" + else + agentapi server --type copilot --term-width 120 --term-height 40 -- copilot + fi fi fi } From e6a513880576697a744450813d067c78c6a2e53c Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Thu, 2 Oct 2025 08:44:45 -0500 Subject: [PATCH 28/58] chore: bun run fmt --- registry/coder-labs/modules/copilot-cli/scripts/start.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/scripts/start.sh b/registry/coder-labs/modules/copilot-cli/scripts/start.sh index 54f29fb32..c2ed396c4 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/start.sh +++ b/registry/coder-labs/modules/copilot-cli/scripts/start.sh @@ -28,7 +28,7 @@ validate_copilot_installation() { build_initial_prompt() { local initial_prompt="" - + if [ -n "$ARG_AI_PROMPT" ]; then if [ -n "$ARG_SYSTEM_PROMPT" ]; then initial_prompt=" @@ -40,7 +40,7 @@ $ARG_AI_PROMPT" initial_prompt="$ARG_AI_PROMPT" fi fi - + echo "$initial_prompt" } From fac2803e139b324f3bc728bf080389350a422d0e Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Thu, 2 Oct 2025 09:21:00 -0500 Subject: [PATCH 29/58] fix: remove system wrapper from system prompt --- registry/coder-labs/modules/copilot-cli/scripts/start.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/scripts/start.sh b/registry/coder-labs/modules/copilot-cli/scripts/start.sh index c2ed396c4..44787b47e 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/start.sh +++ b/registry/coder-labs/modules/copilot-cli/scripts/start.sh @@ -31,9 +31,7 @@ build_initial_prompt() { if [ -n "$ARG_AI_PROMPT" ]; then if [ -n "$ARG_SYSTEM_PROMPT" ]; then - initial_prompt=" -$ARG_SYSTEM_PROMPT - + initial_prompt="$ARG_SYSTEM_PROMPT $ARG_AI_PROMPT" else From bff6ec1a6b7df07565471f804e118436546375f8 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Thu, 2 Oct 2025 11:30:13 -0500 Subject: [PATCH 30/58] docs: cleanup readme, and align with other ai modules --- .../coder-labs/modules/copilot-cli/README.md | 101 ++---------------- 1 file changed, 9 insertions(+), 92 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/README.md b/registry/coder-labs/modules/copilot-cli/README.md index d138d5cbb..2e6f15142 100644 --- a/registry/coder-labs/modules/copilot-cli/README.md +++ b/registry/coder-labs/modules/copilot-cli/README.md @@ -27,10 +27,9 @@ module "copilot_cli" { - **Node.js v22+** and **npm v10+** - **Active Copilot subscription** (GitHub Copilot Pro, Pro+, Business, or Enterprise) - **GitHub authentication** via one of: - - Direct token via `github_token` variable (highest priority) + - Direct token via `github_token` variable - Coder external authentication (recommended) - - GitHub CLI (`gh auth login`) - - Or use interactive login in Copilot CLI + - Interactive login in Copilot CLI ## Examples @@ -53,6 +52,7 @@ module "copilot_cli" { agent_id = coder_agent.example.id workdir = "/home/coder/project" + github_token = "your_github_token_here" # Or use data.coder_external_auth.github.access_token ai_prompt = data.coder_parameter.ai_prompt.value copilot_model = "claude-sonnet-4.5" allow_all_tools = true @@ -102,8 +102,6 @@ module "copilot_cli" { ### Direct Token Authentication -Use a GitHub token directly (OAuth token or Personal Access Token): - ```tf module "copilot_cli" { source = "registry.coder.com/coder-labs/copilot-cli/coder" @@ -114,15 +112,6 @@ module "copilot_cli" { } ``` -## Configuration Files - -This module creates and manages configuration files in `~/.copilot/`: - -- `config.json` - Copilot CLI settings (banner, theme, trusted directories) -- `mcp-config.json` - Model Context Protocol server definitions - -The module automatically configures GitHub and Coder MCP servers, and merges any custom MCP servers you provide via `mcp_config`. - ### Standalone Mode Run and configure Copilot CLI as a standalone tool in your workspace. @@ -160,98 +149,26 @@ module "copilot_cli" { } ``` -### System Prompt Configuration - -You can customize the behavior of Copilot CLI by providing a system prompt that will be combined with task prompts: - -```tf -module "copilot_cli" { - source = "registry.coder.com/coder-labs/copilot-cli/coder" - version = "1.0.0" - agent_id = coder_agent.example.id - workdir = "/home/coder/project" - - system_prompt = <<-EOT - You are a senior software engineer helping with code development. - Always prioritize: - - Code quality and best practices - - Security considerations - - Performance optimization - - Clear documentation and comments - - When suggesting changes, explain the reasoning behind your recommendations. - Send a task status update to notify the user that you are ready for input, and then wait for user input. - EOT -} -``` - ## Authentication -This module works with multiple GitHub authentication methods in priority order: - -**1. Direct Token :** - -- **`github_token` variable**: Provide a GitHub OAuth token or Personal Access Token directly to the module +The module supports multiple authentication methods (in priority order): -**2. Automatic detection:** - -- **Coder External Auth**: OAuth tokens from GitHub external authentication configured in Coder -- **GitHub CLI**: OAuth tokens from `gh auth login` in the workspace - -**3. Interactive fallback:** - -- **Interactive login**: If no authentication is found, Copilot CLI will prompt users to login via the `/login` slash command - -**No setup required** for automatic methods - the module detects and uses whatever authentication is available. +1. **Direct Token** - Pass `github_token` variable (OAuth or Personal Access Token) +2. **Coder External Auth** - Automatic if GitHub external auth is configured in Coder +3. **Interactive** - Copilot CLI prompts for login via `/login` command if no auth found > **Note**: OAuth tokens work best with Copilot CLI. Personal Access Tokens may have limited functionality. ## Session Resumption -By default (`resume_session = true`), this module automatically resumes the latest Copilot CLI session when the workspace is restarted. This provides a seamless experience where: - -- **Previous conversations continue** - No need to re-establish context -- **No duplicate prompts** - Initial prompts are only sent on first workspace creation -- **Workspace restart handling** - Automatically detects and resumes existing sessions - -```tf -module "copilot_cli" { - source = "registry.coder.com/coder-labs/copilot-cli/coder" - version = "1.0.0" - agent_id = coder_agent.example.id - workdir = "/home/coder/project" - - resume_session = true # Default: automatically resume sessions - # resume_session = false # Always start fresh sessions -} -``` +By default, the module resumes the latest Copilot CLI session when the workspace restarts. Set `resume_session = false` to always start fresh sessions. ## Task Reporting -When `report_tasks = true` (default), this module automatically configures and starts the **Coder MCP server** for task reporting integration: - -- **Automatic Configuration**: The Coder MCP server is added to the MCP configuration automatically - -- **Task Status Updates**: Copilot CLI can report task progress to the Coder UI -- **No Manual Setup**: Works out-of-the-box with Coder's task reporting system -- **Custom MCP Compatible**: If you provide custom `mcp_config`, the Coder MCP server is added alongside your custom servers - -The Coder MCP server enables Copilot CLI to: - -- Report task status (working, complete, failure) -- Send progress updates to the Coder dashboard -- Integrate with Coder's AI task workflow system +When enabled (default), Copilot CLI can report task progress to the Coder UI using [AgentAPI](https://github.com/coder/agentapi). Custom MCP servers provided via `mcp_config` are merged with the Coder MCP server automatically. To disable task reporting, set `report_tasks = false`. -### MCP Server Configuration - -This module automatically configures MCP servers by: - -1. **Writing MCP Configuration**: Creates `~/.copilot/mcp-config.json` with the correct format -2. **Wrapper Script**: Creates `/tmp/coder-mcp-server.sh` that sets environment variables and runs `coder exp mcp server` -3. **Merging Custom Servers**: Adds any custom MCP servers you provide via the `mcp_config` variable - ## Troubleshooting If you encounter any issues, check the log files in the `~/.copilot-module` directory within your workspace for detailed information. From e0d560c54158aab348c634a34bce2a54e5f0eb18 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Thu, 2 Oct 2025 11:36:41 -0500 Subject: [PATCH 31/58] feat: enhance system prompt for Copilot CLI with detailed tool selection and task reporting guidelines --- .../coder-labs/modules/copilot-cli/README.md | 5 ----- .../coder-labs/modules/copilot-cli/main.tf | 22 ++++++++++++++++++- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/README.md b/registry/coder-labs/modules/copilot-cli/README.md index 2e6f15142..fb5d65cb8 100644 --- a/registry/coder-labs/modules/copilot-cli/README.md +++ b/registry/coder-labs/modules/copilot-cli/README.md @@ -58,11 +58,6 @@ module "copilot_cli" { allow_all_tools = true resume_session = true - system_prompt = <<-EOT - You are a helpful AI coding assistant working in a development environment. - Always follow best practices and provide clear explanations for your suggestions. - Focus on writing clean, maintainable code and helping with debugging tasks. - Send a task status update to notify the user that you are ready for input, and then wait for user input. EOT trusted_directories = ["/home/coder", "/tmp"] diff --git a/registry/coder-labs/modules/copilot-cli/main.tf b/registry/coder-labs/modules/copilot-cli/main.tf index d46a04a64..75bebc092 100644 --- a/registry/coder-labs/modules/copilot-cli/main.tf +++ b/registry/coder-labs/modules/copilot-cli/main.tf @@ -56,7 +56,27 @@ variable "ai_prompt" { variable "system_prompt" { type = string description = "The system prompt to use for the Copilot CLI server." - default = "You are a helpful AI assistant that helps with coding tasks. Always provide clear explanations and follow best practices. Send a task status update to notify the user that you are ready for input, and then wait for user input." + default = <<-EOT + You are a helpful AI assistant that helps with coding tasks. Always provide clear explanations and follow best practices. + + -- Tool Selection -- + - coder_report_task: providing status updates or requesting user input. + - playwright: previewing your changes after you made them to confirm it worked as expected + - desktop-commander: use only for commands that keep running (servers, dev watchers, GUI apps). + - Built-in tools: use for everything else (file operations, git commands, builds & installs, one-off shell commands) + + Remember this decision rule: + - Stays running? → desktop-commander + - Finishes immediately? → built-in tools + + -- 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 } variable "trusted_directories" { From 8a4c3c7bee3f06239dfa1e48d83361a91455622e Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Thu, 2 Oct 2025 12:02:52 -0500 Subject: [PATCH 32/58] chore: update copilot version to 0.1.0 --- registry/coder-labs/modules/copilot-cli/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/README.md b/registry/coder-labs/modules/copilot-cli/README.md index fb5d65cb8..8cad99e8d 100644 --- a/registry/coder-labs/modules/copilot-cli/README.md +++ b/registry/coder-labs/modules/copilot-cli/README.md @@ -13,7 +13,7 @@ Run [GitHub Copilot CLI](https://docs.github.com/copilot/concepts/agents/about-c ```tf module "copilot_cli" { source = "registry.coder.com/coder-labs/copilot-cli/coder" - version = "1.0.0" + version = "0.1.0" agent_id = coder_agent.example.id workdir = "/home/coder/project" } @@ -48,7 +48,7 @@ data "coder_parameter" "ai_prompt" { module "copilot_cli" { source = "registry.coder.com/coder-labs/copilot-cli/coder" - version = "1.0.0" + version = "0.1.0" agent_id = coder_agent.example.id workdir = "/home/coder/project" @@ -71,7 +71,7 @@ For more controlled environments where you want to specify exact tools: ```tf module "copilot_cli" { source = "registry.coder.com/coder-labs/copilot-cli/coder" - version = "1.0.0" + version = "0.1.0" agent_id = coder_agent.example.id workdir = "/home/coder/project" @@ -100,7 +100,7 @@ module "copilot_cli" { ```tf module "copilot_cli" { source = "registry.coder.com/coder-labs/copilot-cli/coder" - version = "1.0.0" + version = "0.1.0" agent_id = coder_agent.example.id workdir = "/home/coder/project" github_token = "your_github_token_here" # Or use data.coder_external_auth.github.access_token @@ -114,7 +114,7 @@ Run and configure Copilot CLI as a standalone tool in your workspace. ```tf module "copilot_cli" { source = "registry.coder.com/coder-labs/copilot-cli/coder" - version = "1.0.0" + version = "0.1.0" agent_id = coder_agent.example.id workdir = "/home/coder" report_tasks = false @@ -129,7 +129,7 @@ You can customize the entire Copilot CLI configuration: ```tf module "copilot_cli" { source = "registry.coder.com/coder-labs/copilot-cli/coder" - version = "1.0.0" + version = "0.1.0" agent_id = coder_agent.example.id workdir = "/home/coder/projects" From b3dda602f47f2eab3182727445a5bc5ca7d19748 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Thu, 2 Oct 2025 13:03:30 -0500 Subject: [PATCH 33/58] test: rewrite tests --- .../coder-labs/modules/copilot-cli/README.md | 2 - .../copilot-cli/copilot-cli.tftest.hcl | 217 +++++------ .../modules/copilot-cli/main.test.ts | 360 ++++-------------- 3 files changed, 163 insertions(+), 416 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/README.md b/registry/coder-labs/modules/copilot-cli/README.md index 8cad99e8d..243f07053 100644 --- a/registry/coder-labs/modules/copilot-cli/README.md +++ b/registry/coder-labs/modules/copilot-cli/README.md @@ -58,8 +58,6 @@ module "copilot_cli" { allow_all_tools = true resume_session = true - EOT - trusted_directories = ["/home/coder", "/tmp"] } ``` diff --git a/registry/coder-labs/modules/copilot-cli/copilot-cli.tftest.hcl b/registry/coder-labs/modules/copilot-cli/copilot-cli.tftest.hcl index fef1cd002..781662762 100644 --- a/registry/coder-labs/modules/copilot-cli/copilot-cli.tftest.hcl +++ b/registry/coder-labs/modules/copilot-cli/copilot-cli.tftest.hcl @@ -1,238 +1,197 @@ -run "required_variables" { +run "defaults_are_correct" { command = plan variables { - agent_id = "test-agent-id" - workdir = "/home/coder" - external_auth_id = "github" + agent_id = "test-agent" + workdir = "/home/coder" } assert { - condition = var.agent_id == "test-agent-id" - error_message = "Agent ID should be set correctly" + condition = var.copilot_model == "claude-sonnet-4" + error_message = "Default model should be 'claude-sonnet-4'" } assert { - condition = var.workdir == "/home/coder" - error_message = "Workdir should be set correctly" + condition = var.report_tasks == true + error_message = "Task reporting should be enabled by default" } assert { - condition = var.external_auth_id == "github" - error_message = "External auth ID should be set correctly" + condition = var.resume_session == true + error_message = "Session resumption should be enabled by default" } -} - -run "minimal_config" { - command = plan - variables { - agent_id = "test-agent-id" - workdir = "/home/coder" - external_auth_id = "github" + assert { + condition = var.allow_all_tools == false + error_message = "allow_all_tools should be disabled by default" } assert { condition = resource.coder_env.mcp_app_status_slug.name == "CODER_MCP_APP_STATUS_SLUG" - error_message = "Status slug environment variable not configured correctly" + error_message = "Status slug env var should be created" } assert { condition = resource.coder_env.mcp_app_status_slug.value == "copilot-cli" error_message = "Status slug value should be 'copilot-cli'" } - - assert { - condition = var.copilot_model == "claude-sonnet-4" - error_message = "Default model should be 'claude-sonnet-4'" - } - - assert { - condition = var.report_tasks == true - error_message = "Task reporting should be enabled by default" - } } -run "custom_model" { +run "github_token_creates_env_var" { command = plan variables { - agent_id = "test-agent-id" - workdir = "/home/coder" - external_auth_id = "github" - copilot_model = "claude-sonnet-4.5" + agent_id = "test-agent" + workdir = "/home/coder" + github_token = "test_github_token_abc123" } assert { - condition = var.copilot_model == "claude-sonnet-4.5" - error_message = "Custom model should be set correctly" + condition = length(resource.coder_env.github_token) == 1 + error_message = "github_token env var should be created when token is provided" } -} -run "custom_copilot_config" { - command = plan - - variables { - agent_id = "test-agent-id" - workdir = "/home/coder" - external_auth_id = "github" - copilot_config = jsonencode({ - banner = "auto" - theme = "light" - trusted_folders = ["/home/coder", "/workspace"] - }) + assert { + condition = resource.coder_env.github_token[0].name == "GITHUB_TOKEN" + error_message = "github_token env var name should be 'GITHUB_TOKEN'" } assert { - condition = var.copilot_config != "" - error_message = "Custom copilot config should be provided" + condition = resource.coder_env.github_token[0].value == "test_github_token_abc123" + error_message = "github_token env var value should match input" } } -run "trusted_directories" { +run "github_token_not_created_when_empty" { command = plan variables { - agent_id = "test-agent-id" - workdir = "/home/coder" - external_auth_id = "github" - trusted_directories = ["/workspace", "/projects"] + agent_id = "test-agent" + workdir = "/home/coder" + github_token = "" } assert { - condition = length(var.trusted_directories) == 2 - error_message = "Trusted directories should be set correctly" + condition = length(resource.coder_env.github_token) == 0 + error_message = "github_token env var should not be created when empty" } } -run "mcp_config" { +run "copilot_model_env_var_for_non_default" { command = plan variables { - agent_id = "test-agent-id" - workdir = "/home/coder" - external_auth_id = "github" - mcp_config = jsonencode({ - mcpServers = { - custom = { - command = "custom-server" - args = ["--config", "custom.json"] - } - } - }) + agent_id = "test-agent" + workdir = "/home/coder" + copilot_model = "claude-sonnet-4.5" } assert { - condition = var.mcp_config != "" - error_message = "Custom MCP config should be provided" - } -} - -run "tool_permissions" { - command = plan - - variables { - agent_id = "test-agent-id" - workdir = "/home/coder" - external_auth_id = "github" - allow_tools = ["fs_read", "fs_write"] - deny_tools = ["execute_bash"] + condition = length(resource.coder_env.copilot_model) == 1 + error_message = "copilot_model env var should be created for non-default model" } assert { - condition = length(var.allow_tools) == 2 - error_message = "Allow tools should be set correctly" + condition = resource.coder_env.copilot_model[0].name == "COPILOT_MODEL" + error_message = "copilot_model env var name should be 'COPILOT_MODEL'" } assert { - condition = length(var.deny_tools) == 1 - error_message = "Deny tools should be set correctly" + condition = resource.coder_env.copilot_model[0].value == "claude-sonnet-4.5" + error_message = "copilot_model env var value should match input" } } -run "ui_customization" { +run "copilot_model_not_created_for_default" { command = plan variables { - agent_id = "test-agent-id" - workdir = "/home/coder" - external_auth_id = "github" - order = 5 - group = "AI Tools" - icon = "/icon/custom-copilot.svg" + agent_id = "test-agent" + workdir = "/home/coder" + copilot_model = "claude-sonnet-4" } assert { - condition = var.order == 5 - error_message = "Order should be set correctly" + condition = length(resource.coder_env.copilot_model) == 0 + error_message = "copilot_model env var should not be created for default model" } +} - assert { - condition = var.group == "AI Tools" - error_message = "Group should be set correctly" +run "model_validation_accepts_valid_models" { + command = plan + + variables { + agent_id = "test-agent" + workdir = "/home/coder" + copilot_model = "gpt-5" } assert { - condition = var.icon == "/icon/custom-copilot.svg" - error_message = "Icon should be set correctly" + condition = contains(["claude-sonnet-4", "claude-sonnet-4.5", "gpt-5"], var.copilot_model) + error_message = "Model should be one of the valid options" } } -run "install_scripts" { +run "copilot_config_merges_with_trusted_directories" { command = plan variables { - agent_id = "test-agent-id" - workdir = "/home/coder" - external_auth_id = "github" - pre_install_script = "echo 'Pre-install setup'" - post_install_script = "echo 'Post-install cleanup'" + agent_id = "test-agent" + workdir = "/home/coder/project" + trusted_directories = ["/workspace", "/data"] } assert { - condition = var.pre_install_script == "echo 'Pre-install setup'" - error_message = "Pre-install script should be set correctly" + condition = length(local.final_copilot_config) > 0 + error_message = "final_copilot_config should be computed" } + # Verify workdir is trimmed of trailing slash assert { - condition = var.post_install_script == "echo 'Post-install cleanup'" - error_message = "Post-install script should be set correctly" + condition = local.workdir == "/home/coder/project" + error_message = "workdir should be trimmed of trailing slash" } } -run "model_validation" { +run "custom_copilot_config_overrides_default" { command = plan variables { - agent_id = "test-agent-id" - workdir = "/home/coder" - external_auth_id = "github" - copilot_model = "gpt-5" + agent_id = "test-agent" + workdir = "/home/coder" + copilot_config = jsonencode({ + banner = "always" + theme = "dark" + trusted_folders = ["/custom"] + }) } assert { - condition = contains(["claude-sonnet-4", "claude-sonnet-4.5", "gpt-5"], var.copilot_model) - error_message = "Model should be one of the valid options" + condition = var.copilot_config != "" + error_message = "Custom copilot config should be set" + } + + assert { + condition = local.final_copilot_config == var.copilot_config + error_message = "Custom copilot config should override default" } } -run "task_reporting_disabled" { +run "app_slug_is_consistent" { command = plan variables { - agent_id = "test-agent-id" - workdir = "/home/coder" - external_auth_id = "github" - report_tasks = false + agent_id = "test-agent" + workdir = "/home/coder" } assert { - condition = var.report_tasks == false - error_message = "Task reporting should be disabled when set to false" + condition = local.app_slug == "copilot-cli" + error_message = "app_slug should be 'copilot-cli'" } assert { - condition = resource.coder_env.mcp_app_status_slug.name == "CODER_MCP_APP_STATUS_SLUG" - error_message = "Status slug should still be configured even when task reporting is disabled" + condition = local.module_dir_name == ".copilot-module" + error_message = "module_dir_name should be '.copilot-module'" } -} \ No newline at end of file +} diff --git a/registry/coder-labs/modules/copilot-cli/main.test.ts b/registry/coder-labs/modules/copilot-cli/main.test.ts index 46dbfe0f9..16565d873 100644 --- a/registry/coder-labs/modules/copilot-cli/main.test.ts +++ b/registry/coder-labs/modules/copilot-cli/main.test.ts @@ -1,59 +1,24 @@ -import { describe, it, expect } from "bun:test"; +import { describe, expect, it } from "bun:test"; import { + findResourceInstance, runTerraformApply, runTerraformInit, - findResourceInstance, + testRequiredVariables, } from "~test"; -import path from "path"; - -const moduleDir = path.resolve(__dirname); -const requiredVars = { - agent_id: "test-agent-id", - workdir: "/home/coder", - external_auth_id: "github", -}; +describe("copilot-cli", async () => { + await runTerraformInit(import.meta.dir); -const fullConfigVars = { - agent_id: "test-agent-id", - workdir: "/home/coder", - external_auth_id: "github", - copilot_model: "claude-sonnet-4.5", - report_tasks: true, - order: 1, - group: "AI Tools", - icon: "/icon/custom-copilot.svg", - pre_install_script: "echo 'Starting pre-install'", - post_install_script: "echo 'Completed post-install'", - copilot_config: JSON.stringify({ - banner: "auto", - theme: "light", - trusted_folders: ["/home/coder", "/workspace"], - }), - mcp_config: JSON.stringify({ - mcpServers: { - github: { - command: "@github/copilot-mcp-github", - env: { - GITHUB_TOKEN: "${GITHUB_TOKEN}", - }, - }, - custom: { - command: "custom-server", - args: ["--config", "custom.json"], - }, - }, - }), - trusted_directories: '["/workspace", "/projects"]', - allow_tools: '["fs_read", "fs_write"]', - deny_tools: '["execute_bash"]', -}; - -describe("copilot-cli module", async () => { - await runTerraformInit(moduleDir); + testRequiredVariables(import.meta.dir, { + agent_id: "test-agent", + workdir: "/home/coder", + }); - it("works with required variables", async () => { - const state = await runTerraformApply(moduleDir, requiredVars); + it("creates mcp_app_status_slug env var", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "test-agent", + workdir: "/home/coder", + }); const statusSlugEnv = findResourceInstance( state, @@ -65,264 +30,89 @@ describe("copilot-cli module", async () => { expect(statusSlugEnv.value).toBe("copilot-cli"); }); - it("creates required environment variables", async () => { - const state = await runTerraformApply(moduleDir, fullConfigVars); + it("creates github_token env var with correct value", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "test-agent", + workdir: "/home/coder", + github_token: "test_token_12345", + }); - const statusSlugEnv = findResourceInstance( + const githubTokenEnv = findResourceInstance( state, "coder_env", - "mcp_app_status_slug", + "github_token", ); - expect(statusSlugEnv).toBeDefined(); - expect(statusSlugEnv.name).toBe("CODER_MCP_APP_STATUS_SLUG"); - expect(statusSlugEnv.value).toBe("copilot-cli"); + expect(githubTokenEnv).toBeDefined(); + expect(githubTokenEnv.name).toBe("GITHUB_TOKEN"); + expect(githubTokenEnv.value).toBe("test_token_12345"); }); - it("uses default model when not specified", async () => { - const state = await runTerraformApply(moduleDir, requiredVars); + it("does not create github_token env var when empty", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "test-agent", + workdir: "/home/coder", + github_token: "", + }); - const statusSlugEnv = findResourceInstance( - state, - "coder_env", - "mcp_app_status_slug", + const githubTokenEnvs = state.resources.filter( + (r) => r.type === "coder_env" && r.name === "github_token", ); - expect(statusSlugEnv).toBeDefined(); + expect(githubTokenEnvs.length).toBe(0); }); - it("supports custom copilot model", async () => { - const customModelVars = { - ...requiredVars, + it("creates copilot_model env var for non-default models", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "test-agent", + workdir: "/home/coder", copilot_model: "claude-sonnet-4.5", - }; + }); - const state = await runTerraformApply(moduleDir, customModelVars); - - const statusSlugEnv = findResourceInstance( - state, - "coder_env", - "mcp_app_status_slug", - ); - expect(statusSlugEnv).toBeDefined(); + const modelEnv = findResourceInstance(state, "coder_env", "copilot_model"); + expect(modelEnv).toBeDefined(); + expect(modelEnv.name).toBe("COPILOT_MODEL"); + expect(modelEnv.value).toBe("claude-sonnet-4.5"); }); - it("supports custom copilot configuration", async () => { - const customConfigVars = { - ...requiredVars, - copilot_config: JSON.stringify({ - banner: "auto", - theme: "dark", - trusted_folders: ["/home/coder", "/workspace"], - }), - }; - - const state = await runTerraformApply(moduleDir, customConfigVars); + it("does not create copilot_model env var for default model", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "test-agent", + workdir: "/home/coder", + copilot_model: "claude-sonnet-4", + }); - const statusSlugEnv = findResourceInstance( - state, - "coder_env", - "mcp_app_status_slug", + const modelEnvs = state.resources.filter( + (r) => r.type === "coder_env" && r.name === "copilot_model", ); - expect(statusSlugEnv).toBeDefined(); + expect(modelEnvs.length).toBe(0); }); - it("supports trusted directories", async () => { - const trustedDirsVars = { - ...requiredVars, - trusted_directories: '["/workspace", "/projects", "/data"]', - }; + it("creates coder_script resources via agentapi module", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "test-agent", + workdir: "/home/coder", + }); - const state = await runTerraformApply(moduleDir, trustedDirsVars); - - const statusSlugEnv = findResourceInstance( - state, - "coder_env", - "mcp_app_status_slug", - ); - expect(statusSlugEnv).toBeDefined(); + // The agentapi module should create coder_script resources for install and start + const scripts = state.resources.filter((r) => r.type === "coder_script"); + expect(scripts.length).toBeGreaterThan(0); }); - it("supports custom MCP configuration", async () => { - const mcpConfigVars = { - ...requiredVars, - mcp_config: JSON.stringify({ - mcpServers: { - filesystem: { - command: "npx", - args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"], - }, - github: { - command: "@github/copilot-mcp-github", - env: { - GITHUB_TOKEN: "${GITHUB_TOKEN}", - }, - }, - }, + it("validates copilot_model accepts valid values", async () => { + // Test valid models don't throw errors + await expect( + runTerraformApply(import.meta.dir, { + agent_id: "test-agent", + workdir: "/home/coder", + copilot_model: "gpt-5", }), - }; - - const state = await runTerraformApply(moduleDir, mcpConfigVars); - - const statusSlugEnv = findResourceInstance( - state, - "coder_env", - "mcp_app_status_slug", - ); - expect(statusSlugEnv).toBeDefined(); - }); - - it("supports tool permissions", async () => { - const toolPermissionsVars = { - ...requiredVars, - allow_tools: '["fs_read", "fs_write", "execute_bash"]', - deny_tools: '["rm", "sudo"]', - }; - - const state = await runTerraformApply(moduleDir, toolPermissionsVars); - - const statusSlugEnv = findResourceInstance( - state, - "coder_env", - "mcp_app_status_slug", - ); - expect(statusSlugEnv).toBeDefined(); - }); - - it("supports UI customization options", async () => { - const uiCustomVars = { - ...requiredVars, - order: 5, - group: "Custom AI Tools", - icon: "/icon/custom-copilot-icon.svg", - }; - - const state = await runTerraformApply(moduleDir, uiCustomVars); - - const statusSlugEnv = findResourceInstance( - state, - "coder_env", - "mcp_app_status_slug", - ); - expect(statusSlugEnv).toBeDefined(); - }); - - it("supports pre and post install scripts", async () => { - const scriptVars = { - ...requiredVars, - pre_install_script: "echo 'Pre-install setup for Copilot CLI'", - post_install_script: "echo 'Post-install cleanup for Copilot CLI'", - }; - - const state = await runTerraformApply(moduleDir, scriptVars); - - const statusSlugEnv = findResourceInstance( - state, - "coder_env", - "mcp_app_status_slug", - ); - expect(statusSlugEnv).toBeDefined(); - }); - - it("handles task reporting disabled", async () => { - const noReportingVars = { - ...requiredVars, - report_tasks: false, - }; - - const state = await runTerraformApply(moduleDir, noReportingVars); - - const statusSlugEnv = findResourceInstance( - state, - "coder_env", - "mcp_app_status_slug", - ); - expect(statusSlugEnv).toBeDefined(); - expect(statusSlugEnv.value).toBe("copilot-cli"); - }); - - it("supports external auth configuration", async () => { - const customAuthVars = { - ...requiredVars, - external_auth_id: "custom-github", - }; - - const state = await runTerraformApply(moduleDir, customAuthVars); - - const statusSlugEnv = findResourceInstance( - state, - "coder_env", - "mcp_app_status_slug", - ); - expect(statusSlugEnv).toBeDefined(); - }); - - it("supports system prompt configuration", async () => { - const systemPromptVars = { - ...requiredVars, - system_prompt: - "You are a helpful AI assistant that focuses on code quality and best practices.", - }; - - const state = await runTerraformApply(moduleDir, systemPromptVars); - - const statusSlugEnv = findResourceInstance( - state, - "coder_env", - "mcp_app_status_slug", - ); - expect(statusSlugEnv).toBeDefined(); - }); - - it("works with full configuration", async () => { - const state = await runTerraformApply(moduleDir, fullConfigVars); + ).resolves.toBeDefined(); - const statusSlugEnv = findResourceInstance( - state, - "coder_env", - "mcp_app_status_slug", - ); - expect(statusSlugEnv).toBeDefined(); - expect(statusSlugEnv.name).toBe("CODER_MCP_APP_STATUS_SLUG"); - expect(statusSlugEnv.value).toBe("copilot-cli"); - }); - - it("supports github_token variable", async () => { - const tokenVars = { - ...requiredVars, - github_token: "test_github_token_123", - }; - - const state = await runTerraformApply(moduleDir, tokenVars); - - const statusSlugEnv = findResourceInstance( - state, - "coder_env", - "mcp_app_status_slug", - ); - expect(statusSlugEnv).toBeDefined(); - - const githubTokenEnv = findResourceInstance( - state, - "coder_env", - "github_token", - ); - expect(githubTokenEnv).toBeDefined(); - expect(githubTokenEnv.name).toBe("GITHUB_TOKEN"); - expect(githubTokenEnv.value).toBe("test_github_token_123"); - }); - - it("supports resume session configuration", async () => { - const resumeSessionVars = { - ...requiredVars, - resume_session: false, - }; - - const state = await runTerraformApply(moduleDir, resumeSessionVars); - - const statusSlugEnv = findResourceInstance( - state, - "coder_env", - "mcp_app_status_slug", - ); - expect(statusSlugEnv).toBeDefined(); + await expect( + runTerraformApply(import.meta.dir, { + agent_id: "test-agent", + workdir: "/home/coder", + copilot_model: "claude-sonnet-4.5", + }), + ).resolves.toBeDefined(); }); }); From 032a04d2d45976177a82db48f9b48b7b25b11dfb Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Thu, 2 Oct 2025 13:15:27 -0500 Subject: [PATCH 34/58] docs: update README for Copilot CLI module with new MCP server configurations and usage notes --- .../coder-labs/modules/copilot-cli/README.md | 31 ++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/README.md b/registry/coder-labs/modules/copilot-cli/README.md index 243f07053..620f1044b 100644 --- a/registry/coder-labs/modules/copilot-cli/README.md +++ b/registry/coder-labs/modules/copilot-cli/README.md @@ -78,9 +78,25 @@ module "copilot_cli" { mcp_config = jsonencode({ mcpServers = { - custom_server = { - command = "npx" - args = ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/allowed/files"] + filesystem = { + command = "npx" + args = ["-y", "@modelcontextprotocol/server-filesystem", "/home/coder/workspace"] + description = "Provides file system access to the workspace" + name = "Filesystem" + timeout = 3000 + type = "local" + tools = ["*"] + trust = true + } + playwright = { + command = "npx" + args = ["-y", "@playwright/mcp@latest", "--headless", "--isolated"] + description = "Browser automation for testing and previewing changes" + name = "Playwright" + timeout = 5000 + type = "local" + tools = ["*"] + trust = false } } }) @@ -93,6 +109,12 @@ module "copilot_cli" { } ``` +> [!NOTE] +> GitHub Copilot CLI does not automatically install MCP servers. You have two options: +> +> - Use `npx -y` in the MCP config (shown above) to auto-install on each run +> - Pre-install MCP servers in `pre_install_script` for faster startup (e.g., `npm install -g @modelcontextprotocol/server-filesystem`) + ### Direct Token Authentication ```tf @@ -150,7 +172,8 @@ The module supports multiple authentication methods (in priority order): 2. **Coder External Auth** - Automatic if GitHub external auth is configured in Coder 3. **Interactive** - Copilot CLI prompts for login via `/login` command if no auth found -> **Note**: OAuth tokens work best with Copilot CLI. Personal Access Tokens may have limited functionality. +> [!NOTE] +> OAuth tokens work best with Copilot CLI. Personal Access Tokens may have limited functionality. ## Session Resumption From 3e4cac5b47c7f5fde88f0b309de762e022edb30c Mon Sep 17 00:00:00 2001 From: DevCats Date: Fri, 3 Oct 2025 08:21:53 -0500 Subject: [PATCH 35/58] Update registry/coder-labs/modules/copilot-cli/README.md Co-authored-by: Atif Ali --- registry/coder-labs/modules/copilot-cli/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/coder-labs/modules/copilot-cli/README.md b/registry/coder-labs/modules/copilot-cli/README.md index 620f1044b..a2b4be047 100644 --- a/registry/coder-labs/modules/copilot-cli/README.md +++ b/registry/coder-labs/modules/copilot-cli/README.md @@ -3,7 +3,7 @@ display_name: Copilot CLI description: GitHub Copilot CLI agent for AI-powered terminal assistance icon: ../../../../.icons/github.svg verified: false -tags: [agent, copilot, ai, github, cli, tasks] +tags: [agent, copilot, ai, github, tasks] --- # Copilot CLI From d15dd644673804c6be28f8fd01673d03d4ed17bd Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Fri, 3 Oct 2025 08:29:22 -0500 Subject: [PATCH 36/58] chore: update default model for copilot --- .../coder-labs/modules/copilot-cli/copilot-cli.tftest.hcl | 6 +++--- registry/coder-labs/modules/copilot-cli/main.test.ts | 2 +- registry/coder-labs/modules/copilot-cli/main.tf | 6 +++--- registry/coder-labs/modules/copilot-cli/scripts/install.sh | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/copilot-cli.tftest.hcl b/registry/coder-labs/modules/copilot-cli/copilot-cli.tftest.hcl index 781662762..2fa197ae6 100644 --- a/registry/coder-labs/modules/copilot-cli/copilot-cli.tftest.hcl +++ b/registry/coder-labs/modules/copilot-cli/copilot-cli.tftest.hcl @@ -7,8 +7,8 @@ run "defaults_are_correct" { } assert { - condition = var.copilot_model == "claude-sonnet-4" - error_message = "Default model should be 'claude-sonnet-4'" + condition = var.copilot_model == "claude-sonnet-4.5" + error_message = "Default model should be 'claude-sonnet-4.5'" } assert { @@ -108,7 +108,7 @@ run "copilot_model_not_created_for_default" { variables { agent_id = "test-agent" workdir = "/home/coder" - copilot_model = "claude-sonnet-4" + copilot_model = "claude-sonnet-4.5" } assert { diff --git a/registry/coder-labs/modules/copilot-cli/main.test.ts b/registry/coder-labs/modules/copilot-cli/main.test.ts index 16565d873..314445c88 100644 --- a/registry/coder-labs/modules/copilot-cli/main.test.ts +++ b/registry/coder-labs/modules/copilot-cli/main.test.ts @@ -77,7 +77,7 @@ describe("copilot-cli", async () => { const state = await runTerraformApply(import.meta.dir, { agent_id: "test-agent", workdir: "/home/coder", - copilot_model: "claude-sonnet-4", + copilot_model: "claude-sonnet-4.5", }); const modelEnvs = state.resources.filter( diff --git a/registry/coder-labs/modules/copilot-cli/main.tf b/registry/coder-labs/modules/copilot-cli/main.tf index 75bebc092..36dc44712 100644 --- a/registry/coder-labs/modules/copilot-cli/main.tf +++ b/registry/coder-labs/modules/copilot-cli/main.tf @@ -33,8 +33,8 @@ variable "github_token" { variable "copilot_model" { type = string - description = "Model to use. Supported values: claude-sonnet-4 (default), claude-sonnet-4.5, gpt-5." - default = "claude-sonnet-4" + description = "Model to use. Supported values: claude-sonnet-4, claude-sonnet-4.5 (default), gpt-5." + default = "claude-sonnet-4.5" validation { condition = contains(["claude-sonnet-4", "claude-sonnet-4.5", "gpt-5"], var.copilot_model) error_message = "copilot_model must be one of: claude-sonnet-4, claude-sonnet-4.5, gpt-5." @@ -213,7 +213,7 @@ resource "coder_env" "mcp_app_status_slug" { } resource "coder_env" "copilot_model" { - count = var.copilot_model != "claude-sonnet-4" ? 1 : 0 + count = var.copilot_model != "claude-sonnet-4.5" ? 1 : 0 agent_id = var.agent_id name = "COPILOT_MODEL" value = var.copilot_model diff --git a/registry/coder-labs/modules/copilot-cli/scripts/install.sh b/registry/coder-labs/modules/copilot-cli/scripts/install.sh index f88f1a81a..a7edaa5f3 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/install.sh +++ b/registry/coder-labs/modules/copilot-cli/scripts/install.sh @@ -196,7 +196,7 @@ add_custom_mcp_servers() { } configure_copilot_model() { - if [ -n "$ARG_COPILOT_MODEL" ] && [ "$ARG_COPILOT_MODEL" != "claude-sonnet-4" ]; then + if [ -n "$ARG_COPILOT_MODEL" ] && [ "$ARG_COPILOT_MODEL" != "claude-sonnet-4.5" ]; then echo "Setting Copilot CLI model to: $ARG_COPILOT_MODEL" copilot config model "$ARG_COPILOT_MODEL" || { echo "WARNING: Failed to set model via copilot config, will use environment variable fallback" From 4786c95f8d1468c24eb0cb653085dfd3501a00eb Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Fri, 3 Oct 2025 08:30:58 -0500 Subject: [PATCH 37/58] chore: add documentation links --- registry/coder-labs/modules/copilot-cli/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/README.md b/registry/coder-labs/modules/copilot-cli/README.md index a2b4be047..c7699d2b9 100644 --- a/registry/coder-labs/modules/copilot-cli/README.md +++ b/registry/coder-labs/modules/copilot-cli/README.md @@ -25,10 +25,10 @@ module "copilot_cli" { ## Prerequisites - **Node.js v22+** and **npm v10+** -- **Active Copilot subscription** (GitHub Copilot Pro, Pro+, Business, or Enterprise) +- **[Active Copilot subscription](https://docs.github.com/en/copilot/about-github-copilot/subscription-plans-for-github-copilot)** (GitHub Copilot Pro, Pro+, Business, or Enterprise) - **GitHub authentication** via one of: - Direct token via `github_token` variable - - Coder external authentication (recommended) + - [Coder external authentication](https://coder.com/docs/admin/external-auth) (recommended) - Interactive login in Copilot CLI ## Examples From f49d808000a8fd31db785c49c7d4b9a357d38671 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Fri, 3 Oct 2025 08:40:31 -0500 Subject: [PATCH 38/58] docs: update README for Copilot CLI module to include direct token authentication details and external auth configuration note --- registry/coder-labs/modules/copilot-cli/README.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/README.md b/registry/coder-labs/modules/copilot-cli/README.md index c7699d2b9..e17bd38d0 100644 --- a/registry/coder-labs/modules/copilot-cli/README.md +++ b/registry/coder-labs/modules/copilot-cli/README.md @@ -19,6 +19,9 @@ module "copilot_cli" { } ``` +> [!IMPORTANT] +> This example assumes you have [Coder external authentication](https://coder.com/docs/admin/external-auth) configured with `id = "github"`. If not, you can provide a direct token using the `github_token` variable. + > [!NOTE] > By default, this module is configured to run the embedded chat interface as a path-based application. In production, we recommend that you configure a [wildcard access URL](https://coder.com/docs/admin/setup#wildcard-access-url) and set `subdomain = true`. See [here](https://coder.com/docs/tutorials/best-practices/security-best-practices#disable-path-based-apps) for more details. @@ -52,7 +55,6 @@ module "copilot_cli" { agent_id = coder_agent.example.id workdir = "/home/coder/project" - github_token = "your_github_token_here" # Or use data.coder_external_auth.github.access_token ai_prompt = data.coder_parameter.ai_prompt.value copilot_model = "claude-sonnet-4.5" allow_all_tools = true @@ -117,13 +119,21 @@ module "copilot_cli" { ### Direct Token Authentication +Use this example when you want to provide a GitHub Personal Access Token instead of using Coder external auth: + ```tf +variable "github_token" { + type = string + description = "GitHub Personal Access Token" + sensitive = true +} + module "copilot_cli" { source = "registry.coder.com/coder-labs/copilot-cli/coder" version = "0.1.0" agent_id = coder_agent.example.id workdir = "/home/coder/project" - github_token = "your_github_token_here" # Or use data.coder_external_auth.github.access_token + github_token = var.github_token } ``` From ab83854e9ba3a3a06380c7bccebc6b1a2cd8e354 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Fri, 3 Oct 2025 08:42:55 -0500 Subject: [PATCH 39/58] docs: clarify standalone mode functionality and usage --- registry/coder-labs/modules/copilot-cli/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/coder-labs/modules/copilot-cli/README.md b/registry/coder-labs/modules/copilot-cli/README.md index e17bd38d0..92ff9ee0b 100644 --- a/registry/coder-labs/modules/copilot-cli/README.md +++ b/registry/coder-labs/modules/copilot-cli/README.md @@ -139,7 +139,7 @@ module "copilot_cli" { ### Standalone Mode -Run and configure Copilot CLI as a standalone tool in your workspace. +Run Copilot CLI as a command-line tool without task reporting or web interface. This installs and configures Copilot CLI, making it available as a CLI app in the Coder agent bar that you can launch to interact with Copilot CLI directly from your terminal. ```tf module "copilot_cli" { From dd14436ecf14853d14c68433c5c6c907c58416d5 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Fri, 3 Oct 2025 08:44:37 -0500 Subject: [PATCH 40/58] docs: combine advanced config and custom config examples --- .../coder-labs/modules/copilot-cli/README.md | 39 +++++++------------ 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/README.md b/registry/coder-labs/modules/copilot-cli/README.md index 92ff9ee0b..001512df6 100644 --- a/registry/coder-labs/modules/copilot-cli/README.md +++ b/registry/coder-labs/modules/copilot-cli/README.md @@ -64,9 +64,9 @@ module "copilot_cli" { } ``` -### Advanced Configuration with Specific Tool Permissions +### Advanced Configuration -For more controlled environments where you want to specify exact tools: +Customize tool permissions, MCP servers, and Copilot CLI settings: ```tf module "copilot_cli" { @@ -75,9 +75,21 @@ module "copilot_cli" { agent_id = coder_agent.example.id workdir = "/home/coder/project" + # Tool permissions allow_tools = ["shell(git)", "shell(npm)", "write"] trusted_directories = ["/home/coder/workspace", "/tmp"] + # Custom Copilot CLI configuration + copilot_config = jsonencode({ + banner = "auto" + theme = "dark" + trusted_folders = [ + "/home/coder/workspace", + "/home/coder/project" + ] + }) + + # MCP server configuration mcp_config = jsonencode({ mcpServers = { filesystem = { @@ -103,6 +115,7 @@ module "copilot_cli" { } }) + # Pre-install Node.js if needed pre_install_script = <<-EOT #!/bin/bash curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - @@ -152,28 +165,6 @@ module "copilot_cli" { } ``` -### Custom Configuration - -You can customize the entire Copilot CLI configuration: - -```tf -module "copilot_cli" { - source = "registry.coder.com/coder-labs/copilot-cli/coder" - version = "0.1.0" - agent_id = coder_agent.example.id - workdir = "/home/coder/projects" - - copilot_config = jsonencode({ - banner = "auto" - theme = "dark" - trusted_folders = [ - "/home/coder/workspace", - "/home/coder/projects" - ] - }) -} -``` - ## Authentication The module supports multiple authentication methods (in priority order): From 74055760a6953b923fb803af5c33c77492b9781f Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Fri, 3 Oct 2025 08:46:31 -0500 Subject: [PATCH 41/58] docs: add note on session resumption requirements for Copilot CLI module --- registry/coder-labs/modules/copilot-cli/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/registry/coder-labs/modules/copilot-cli/README.md b/registry/coder-labs/modules/copilot-cli/README.md index 001512df6..60aa56cd8 100644 --- a/registry/coder-labs/modules/copilot-cli/README.md +++ b/registry/coder-labs/modules/copilot-cli/README.md @@ -180,6 +180,9 @@ The module supports multiple authentication methods (in priority order): By default, the module resumes the latest Copilot CLI session when the workspace restarts. Set `resume_session = false` to always start fresh sessions. +> [!NOTE] +> Session resumption requires persistent storage for the home directory or workspace volume. Without persistent storage, sessions will not resume across workspace restarts. + ## Task Reporting When enabled (default), Copilot CLI can report task progress to the Coder UI using [AgentAPI](https://github.com/coder/agentapi). Custom MCP servers provided via `mcp_config` are merged with the Coder MCP server automatically. From 24454b48d222d0b0c73ed9179a0454f9589a6704 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Fri, 3 Oct 2025 08:49:07 -0500 Subject: [PATCH 42/58] docs: update Copilot CLI README to clarify task reporting --- registry/coder-labs/modules/copilot-cli/README.md | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/README.md b/registry/coder-labs/modules/copilot-cli/README.md index 60aa56cd8..7295f963f 100644 --- a/registry/coder-labs/modules/copilot-cli/README.md +++ b/registry/coder-labs/modules/copilot-cli/README.md @@ -152,7 +152,7 @@ module "copilot_cli" { ### Standalone Mode -Run Copilot CLI as a command-line tool without task reporting or web interface. This installs and configures Copilot CLI, making it available as a CLI app in the Coder agent bar that you can launch to interact with Copilot CLI directly from your terminal. +Run Copilot CLI as a command-line tool without task reporting or web interface. This installs and configures Copilot CLI, making it available as a CLI app in the Coder agent bar that you can launch to interact with Copilot CLI directly from your terminal. Set `report_tasks = false` to disable integration with Coder Tasks. ```tf module "copilot_cli" { @@ -183,12 +183,6 @@ By default, the module resumes the latest Copilot CLI session when the workspace > [!NOTE] > Session resumption requires persistent storage for the home directory or workspace volume. Without persistent storage, sessions will not resume across workspace restarts. -## Task Reporting - -When enabled (default), Copilot CLI can report task progress to the Coder UI using [AgentAPI](https://github.com/coder/agentapi). Custom MCP servers provided via `mcp_config` are merged with the Coder MCP server automatically. - -To disable task reporting, set `report_tasks = false`. - ## Troubleshooting If you encounter any issues, check the log files in the `~/.copilot-module` directory within your workspace for detailed information. From 4e6591b801c73e56e917ab9dad26a90ed288ede0 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Fri, 3 Oct 2025 08:53:47 -0500 Subject: [PATCH 43/58] fix: update copilot model version in tests and tftest file to match the non defaults --- .../coder-labs/modules/copilot-cli/copilot-cli.tftest.hcl | 4 ++-- registry/coder-labs/modules/copilot-cli/main.test.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/copilot-cli.tftest.hcl b/registry/coder-labs/modules/copilot-cli/copilot-cli.tftest.hcl index 2fa197ae6..4fed2f3ab 100644 --- a/registry/coder-labs/modules/copilot-cli/copilot-cli.tftest.hcl +++ b/registry/coder-labs/modules/copilot-cli/copilot-cli.tftest.hcl @@ -83,7 +83,7 @@ run "copilot_model_env_var_for_non_default" { variables { agent_id = "test-agent" workdir = "/home/coder" - copilot_model = "claude-sonnet-4.5" + copilot_model = "claude-sonnet-4" } assert { @@ -97,7 +97,7 @@ run "copilot_model_env_var_for_non_default" { } assert { - condition = resource.coder_env.copilot_model[0].value == "claude-sonnet-4.5" + condition = resource.coder_env.copilot_model[0].value == "claude-sonnet-4" error_message = "copilot_model env var value should match input" } } diff --git a/registry/coder-labs/modules/copilot-cli/main.test.ts b/registry/coder-labs/modules/copilot-cli/main.test.ts index 314445c88..a57945543 100644 --- a/registry/coder-labs/modules/copilot-cli/main.test.ts +++ b/registry/coder-labs/modules/copilot-cli/main.test.ts @@ -64,13 +64,13 @@ describe("copilot-cli", async () => { const state = await runTerraformApply(import.meta.dir, { agent_id: "test-agent", workdir: "/home/coder", - copilot_model: "claude-sonnet-4.5", + copilot_model: "claude-sonnet-4", }); const modelEnv = findResourceInstance(state, "coder_env", "copilot_model"); expect(modelEnv).toBeDefined(); expect(modelEnv.name).toBe("COPILOT_MODEL"); - expect(modelEnv.value).toBe("claude-sonnet-4.5"); + expect(modelEnv.value).toBe("claude-sonnet-4"); }); it("does not create copilot_model env var for default model", async () => { From a4c7b54425a9201baa1b6a17fb656137210a14ad Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Fri, 3 Oct 2025 09:00:02 -0500 Subject: [PATCH 44/58] docs: reorder authentication methods in Copilot CLI README for clarity --- registry/coder-labs/modules/copilot-cli/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/README.md b/registry/coder-labs/modules/copilot-cli/README.md index 7295f963f..ffd733ef9 100644 --- a/registry/coder-labs/modules/copilot-cli/README.md +++ b/registry/coder-labs/modules/copilot-cli/README.md @@ -30,8 +30,8 @@ module "copilot_cli" { - **Node.js v22+** and **npm v10+** - **[Active Copilot subscription](https://docs.github.com/en/copilot/about-github-copilot/subscription-plans-for-github-copilot)** (GitHub Copilot Pro, Pro+, Business, or Enterprise) - **GitHub authentication** via one of: - - Direct token via `github_token` variable - [Coder external authentication](https://coder.com/docs/admin/external-auth) (recommended) + - Direct token via `github_token` variable - Interactive login in Copilot CLI ## Examples @@ -169,8 +169,8 @@ module "copilot_cli" { The module supports multiple authentication methods (in priority order): -1. **Direct Token** - Pass `github_token` variable (OAuth or Personal Access Token) -2. **Coder External Auth** - Automatic if GitHub external auth is configured in Coder +1. **[Coder External Auth](https://coder.com/docs/admin/external-auth) (Recommended)** - Automatic if GitHub external auth is configured in Coder +2. **Direct Token** - Pass `github_token` variable (OAuth or Personal Access Token) 3. **Interactive** - Copilot CLI prompts for login via `/login` command if no auth found > [!NOTE] From d161d968213108a868fd40ed424011b2f038c004 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Fri, 3 Oct 2025 09:01:20 -0500 Subject: [PATCH 45/58] chore: update default AgentAPI version to v0.9.0 in Copilot CLI module --- registry/coder-labs/modules/copilot-cli/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/coder-labs/modules/copilot-cli/main.tf b/registry/coder-labs/modules/copilot-cli/main.tf index 36dc44712..86a5f00de 100644 --- a/registry/coder-labs/modules/copilot-cli/main.tf +++ b/registry/coder-labs/modules/copilot-cli/main.tf @@ -118,7 +118,7 @@ variable "install_agentapi" { variable "agentapi_version" { type = string description = "The version of AgentAPI to install." - default = "v0.8.0" + default = "v0.9.0" } variable "report_tasks" { From 4d1446d05d11e1869497f6959c02c1f759312b8b Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Fri, 3 Oct 2025 09:39:39 -0500 Subject: [PATCH 46/58] fix: fix unbound variable issue when github_token is not set --- registry/coder-labs/modules/copilot-cli/scripts/start.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/coder-labs/modules/copilot-cli/scripts/start.sh b/registry/coder-labs/modules/copilot-cli/scripts/start.sh index 44787b47e..4a91b393d 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/start.sh +++ b/registry/coder-labs/modules/copilot-cli/scripts/start.sh @@ -84,7 +84,7 @@ check_existing_session() { setup_github_authentication() { echo "Setting up GitHub authentication..." - if [ -n "$GITHUB_TOKEN" ]; then + if [ -n "${GITHUB_TOKEN:-}" ]; then export GH_TOKEN="$GITHUB_TOKEN" echo "✓ Using GitHub token from module configuration" return 0 From f23431cc94a6b1900f51f6c9f2e207ce34628a9d Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Fri, 3 Oct 2025 10:00:19 -0500 Subject: [PATCH 47/58] fix: resolve unbound variable issue for GITHUB_TOKEN in install script --- registry/coder-labs/modules/copilot-cli/scripts/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/coder-labs/modules/copilot-cli/scripts/install.sh b/registry/coder-labs/modules/copilot-cli/scripts/install.sh index a7edaa5f3..f2cfa45b0 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/install.sh +++ b/registry/coder-labs/modules/copilot-cli/scripts/install.sh @@ -51,7 +51,7 @@ install_copilot_cli() { check_github_authentication() { echo "Checking GitHub authentication..." - if [ -n "$GITHUB_TOKEN" ]; then + if [ -n "${GITHUB_TOKEN:-}" ]; then echo "✓ GitHub token provided via module configuration" return 0 fi From 88836360acfaa1afdd49be34d70cabd781ad3589 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Fri, 3 Oct 2025 13:20:54 -0500 Subject: [PATCH 48/58] fix: update Copilot CLI configuration to set banner to 'never' --- registry/coder-labs/modules/copilot-cli/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/coder-labs/modules/copilot-cli/README.md b/registry/coder-labs/modules/copilot-cli/README.md index ffd733ef9..230b530c7 100644 --- a/registry/coder-labs/modules/copilot-cli/README.md +++ b/registry/coder-labs/modules/copilot-cli/README.md @@ -81,7 +81,7 @@ module "copilot_cli" { # Custom Copilot CLI configuration copilot_config = jsonencode({ - banner = "auto" + banner = "never" theme = "dark" trusted_folders = [ "/home/coder/workspace", From bb3cb0294b8975b04f66562fddd974940a6a30cf Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Fri, 3 Oct 2025 13:38:01 -0500 Subject: [PATCH 49/58] fix: enhance Copilot CLI configuration to merge trusted directories with custom settings --- .../coder-labs/modules/copilot-cli/README.md | 4 -- .../copilot-cli/copilot-cli.tftest.hcl | 47 +++++++++++++++++-- .../modules/copilot-cli/main.test.ts | 18 +++++++ .../coder-labs/modules/copilot-cli/main.tf | 23 ++++++--- 4 files changed, 78 insertions(+), 14 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/README.md b/registry/coder-labs/modules/copilot-cli/README.md index 230b530c7..7dd8d543e 100644 --- a/registry/coder-labs/modules/copilot-cli/README.md +++ b/registry/coder-labs/modules/copilot-cli/README.md @@ -83,10 +83,6 @@ module "copilot_cli" { copilot_config = jsonencode({ banner = "never" theme = "dark" - trusted_folders = [ - "/home/coder/workspace", - "/home/coder/project" - ] }) # MCP server configuration diff --git a/registry/coder-labs/modules/copilot-cli/copilot-cli.tftest.hcl b/registry/coder-labs/modules/copilot-cli/copilot-cli.tftest.hcl index 4fed2f3ab..a803ebbd7 100644 --- a/registry/coder-labs/modules/copilot-cli/copilot-cli.tftest.hcl +++ b/registry/coder-labs/modules/copilot-cli/copilot-cli.tftest.hcl @@ -159,21 +159,60 @@ run "custom_copilot_config_overrides_default" { variables { agent_id = "test-agent" workdir = "/home/coder" + copilot_config = jsonencode({ + banner = "always" + theme = "dark" + }) + } + + assert { + condition = var.copilot_config != "" + error_message = "Custom copilot config should be set" + } + + assert { + condition = jsondecode(local.final_copilot_config).banner == "always" + error_message = "Custom banner setting should be applied" + } + + assert { + condition = jsondecode(local.final_copilot_config).theme == "dark" + error_message = "Custom theme setting should be applied" + } +} + +run "trusted_directories_merged_with_custom_config" { + command = plan + + variables { + agent_id = "test-agent" + workdir = "/home/coder/project" copilot_config = jsonencode({ banner = "always" theme = "dark" trusted_folders = ["/custom"] }) + trusted_directories = ["/workspace", "/data"] } assert { - condition = var.copilot_config != "" - error_message = "Custom copilot config should be set" + condition = contains(jsondecode(local.final_copilot_config).trusted_folders, "/custom") + error_message = "Custom trusted folder should be included" + } + + assert { + condition = contains(jsondecode(local.final_copilot_config).trusted_folders, "/home/coder/project") + error_message = "Workdir should be included in trusted folders" + } + + assert { + condition = contains(jsondecode(local.final_copilot_config).trusted_folders, "/workspace") + error_message = "trusted_directories should be merged into config" } assert { - condition = local.final_copilot_config == var.copilot_config - error_message = "Custom copilot config should override default" + condition = contains(jsondecode(local.final_copilot_config).trusted_folders, "/data") + error_message = "All trusted_directories should be merged into config" } } diff --git a/registry/coder-labs/modules/copilot-cli/main.test.ts b/registry/coder-labs/modules/copilot-cli/main.test.ts index a57945543..00d31104d 100644 --- a/registry/coder-labs/modules/copilot-cli/main.test.ts +++ b/registry/coder-labs/modules/copilot-cli/main.test.ts @@ -115,4 +115,22 @@ describe("copilot-cli", async () => { }), ).resolves.toBeDefined(); }); + + it("merges trusted_directories with custom copilot_config", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "test-agent", + workdir: "/home/coder/project", + trusted_directories: JSON.stringify(["/workspace", "/data"]), + copilot_config: JSON.stringify({ + banner: "always", + theme: "dark", + trusted_folders: ["/custom"], + }), + }); + + // Verify that the state was created successfully with the merged config + // The actual merging logic is tested in the .tftest.hcl file + expect(state).toBeDefined(); + expect(state.resources).toBeDefined(); + }); }); diff --git a/registry/coder-labs/modules/copilot-cli/main.tf b/registry/coder-labs/modules/copilot-cli/main.tf index 86a5f00de..055d2571c 100644 --- a/registry/coder-labs/modules/copilot-cli/main.tf +++ b/registry/coder-labs/modules/copilot-cli/main.tf @@ -197,13 +197,24 @@ locals { start_script = file("${path.module}/scripts/start.sh") module_dir_name = ".copilot-module" - default_copilot_config = jsonencode({ - banner = "never" - theme = "auto" - trusted_folders = concat([local.workdir], var.trusted_directories) - }) + all_trusted_folders = concat([local.workdir], var.trusted_directories) - final_copilot_config = var.copilot_config != "" ? var.copilot_config : local.default_copilot_config + parsed_custom_config = try(jsondecode(var.copilot_config), {}) + + existing_trusted_folders = try(local.parsed_custom_config.trusted_folders, []) + + merged_copilot_config = merge( + { + banner = "never" + theme = "auto" + }, + local.parsed_custom_config, + { + trusted_folders = concat(local.existing_trusted_folders, local.all_trusted_folders) + } + ) + + final_copilot_config = jsonencode(local.merged_copilot_config) } resource "coder_env" "mcp_app_status_slug" { From 7c1b2a4d6a315ef5e024a836a68a0a0223dd0d74 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Fri, 3 Oct 2025 13:50:25 -0500 Subject: [PATCH 50/58] chore: align directories to match up with examples and tasks --- registry/coder-labs/modules/copilot-cli/README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/README.md b/registry/coder-labs/modules/copilot-cli/README.md index 7dd8d543e..c541fce33 100644 --- a/registry/coder-labs/modules/copilot-cli/README.md +++ b/registry/coder-labs/modules/copilot-cli/README.md @@ -15,7 +15,7 @@ module "copilot_cli" { source = "registry.coder.com/coder-labs/copilot-cli/coder" version = "0.1.0" agent_id = coder_agent.example.id - workdir = "/home/coder/project" + workdir = "/home/coder/projects" } ``` @@ -53,14 +53,14 @@ module "copilot_cli" { source = "registry.coder.com/coder-labs/copilot-cli/coder" version = "0.1.0" agent_id = coder_agent.example.id - workdir = "/home/coder/project" + workdir = "/home/coder/projects" ai_prompt = data.coder_parameter.ai_prompt.value copilot_model = "claude-sonnet-4.5" allow_all_tools = true resume_session = true - trusted_directories = ["/home/coder", "/tmp"] + trusted_directories = ["/home/coder/projects", "/tmp"] } ``` @@ -73,11 +73,11 @@ module "copilot_cli" { source = "registry.coder.com/coder-labs/copilot-cli/coder" version = "0.1.0" agent_id = coder_agent.example.id - workdir = "/home/coder/project" + workdir = "/home/coder/projects" # Tool permissions allow_tools = ["shell(git)", "shell(npm)", "write"] - trusted_directories = ["/home/coder/workspace", "/tmp"] + trusted_directories = ["/home/coder/projects", "/tmp"] # Custom Copilot CLI configuration copilot_config = jsonencode({ @@ -90,7 +90,7 @@ module "copilot_cli" { mcpServers = { filesystem = { command = "npx" - args = ["-y", "@modelcontextprotocol/server-filesystem", "/home/coder/workspace"] + args = ["-y", "@modelcontextprotocol/server-filesystem", "/home/coder/projects"] description = "Provides file system access to the workspace" name = "Filesystem" timeout = 3000 @@ -141,7 +141,7 @@ module "copilot_cli" { source = "registry.coder.com/coder-labs/copilot-cli/coder" version = "0.1.0" agent_id = coder_agent.example.id - workdir = "/home/coder/project" + workdir = "/home/coder/projects" github_token = var.github_token } ``` From 2f65293b660d978d9c7e2bb9e37c3aabab74934f Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Fri, 3 Oct 2025 14:29:30 -0500 Subject: [PATCH 51/58] fix: improve session resumption logic in Copilot CLI script to identify and resume the latest session if more than one session file exists --- .../modules/copilot-cli/scripts/start.sh | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/registry/coder-labs/modules/copilot-cli/scripts/start.sh b/registry/coder-labs/modules/copilot-cli/scripts/start.sh index 4a91b393d..34ec8f576 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/start.sh +++ b/registry/coder-labs/modules/copilot-cli/scripts/start.sh @@ -73,11 +73,23 @@ check_existing_session() { if copilot --help > /dev/null 2>&1; then local session_dir="$HOME/.copilot/history-session-state" if [ -d "$session_dir" ] && [ -n "$(ls "$session_dir"/session_*_*.json 2> /dev/null)" ]; then - echo "Found existing Copilot CLI sessions. Resume mode enabled." - return 0 + local latest_session_file + latest_session_file=$(ls "$session_dir"/session_*_*.json 2> /dev/null | sort -t_ -k3 -n -r | head -n 1) + + if [ -n "$latest_session_file" ]; then + local session_id + session_id=$(basename "$latest_session_file" | sed 's/session_\(.*\)_[0-9]*.json/\1/') + + if [ -n "$session_id" ]; then + echo "Found existing Copilot CLI sessions. Will resume latest: $session_id" + echo "$session_id" + return 0 + fi + fi fi fi fi + echo "" return 1 } @@ -119,13 +131,16 @@ start_agentapi() { build_copilot_args - if check_existing_session; then - echo "Resuming latest Copilot CLI session..." + local session_id + session_id=$(check_existing_session) + + if [ -n "$session_id" ]; then + echo "Resuming Copilot CLI session: $session_id" if [ ${#COPILOT_ARGS[@]} -gt 0 ]; then echo "Copilot arguments: ${COPILOT_ARGS[*]}" - agentapi server --type copilot --term-width 120 --term-height 40 -- copilot --resume "${COPILOT_ARGS[@]}" + agentapi server --type copilot --term-width 120 --term-height 40 -- copilot --resume "$session_id" "${COPILOT_ARGS[@]}" else - agentapi server --type copilot --term-width 120 --term-height 40 -- copilot --resume + agentapi server --type copilot --term-width 120 --term-height 40 -- copilot --resume "$session_id" fi else echo "Starting new Copilot CLI session..." From fe83041e3ffd7b0b40229949d9025ca81a08a211 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Fri, 3 Oct 2025 14:38:08 -0500 Subject: [PATCH 52/58] fix: redirect session resumption message to stderr in Copilot CLI script for better handling --- registry/coder-labs/modules/copilot-cli/scripts/start.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/coder-labs/modules/copilot-cli/scripts/start.sh b/registry/coder-labs/modules/copilot-cli/scripts/start.sh index 34ec8f576..663d8998d 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/start.sh +++ b/registry/coder-labs/modules/copilot-cli/scripts/start.sh @@ -81,7 +81,7 @@ check_existing_session() { session_id=$(basename "$latest_session_file" | sed 's/session_\(.*\)_[0-9]*.json/\1/') if [ -n "$session_id" ]; then - echo "Found existing Copilot CLI sessions. Will resume latest: $session_id" + echo "Found existing Copilot CLI sessions. Will resume latest: $session_id" >&2 echo "$session_id" return 0 fi From c96194ea95dc3f2836454bb406158d8ef0bca2a1 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Mon, 6 Oct 2025 12:51:14 -0500 Subject: [PATCH 53/58] chore: rename module from copilot-cli to copilot --- .../{copilot-cli => copilot}/README.md | 46 +++++++++---------- .../copilot.tftest.hcl} | 8 ++-- .../{copilot-cli => copilot}/main.test.ts | 4 +- .../modules/{copilot-cli => copilot}/main.tf | 24 +++++----- .../scripts/install.sh | 22 ++++----- .../{copilot-cli => copilot}/scripts/start.sh | 12 ++--- .../testdata/copilot-mock.sh | 2 +- 7 files changed, 59 insertions(+), 59 deletions(-) rename registry/coder-labs/modules/{copilot-cli => copilot}/README.md (78%) rename registry/coder-labs/modules/{copilot-cli/copilot-cli.tftest.hcl => copilot/copilot.tftest.hcl} (96%) rename registry/coder-labs/modules/{copilot-cli => copilot}/main.test.ts (97%) rename registry/coder-labs/modules/{copilot-cli => copilot}/main.tf (92%) rename registry/coder-labs/modules/{copilot-cli => copilot}/scripts/install.sh (91%) rename registry/coder-labs/modules/{copilot-cli => copilot}/scripts/start.sh (92%) rename registry/coder-labs/modules/{copilot-cli => copilot}/testdata/copilot-mock.sh (75%) diff --git a/registry/coder-labs/modules/copilot-cli/README.md b/registry/coder-labs/modules/copilot/README.md similarity index 78% rename from registry/coder-labs/modules/copilot-cli/README.md rename to registry/coder-labs/modules/copilot/README.md index c541fce33..f5ff477ec 100644 --- a/registry/coder-labs/modules/copilot-cli/README.md +++ b/registry/coder-labs/modules/copilot/README.md @@ -1,18 +1,18 @@ --- -display_name: Copilot CLI +display_name: Copilot description: GitHub Copilot CLI agent for AI-powered terminal assistance icon: ../../../../.icons/github.svg verified: false tags: [agent, copilot, ai, github, tasks] --- -# Copilot CLI +# Copilot Run [GitHub Copilot CLI](https://docs.github.com/copilot/concepts/agents/about-copilot-cli) in your workspace for AI-powered coding assistance directly from the terminal. This module integrates with [AgentAPI](https://github.com/coder/agentapi) for task reporting in the Coder UI. ```tf -module "copilot_cli" { - source = "registry.coder.com/coder-labs/copilot-cli/coder" +module "copilot" { + source = "registry.coder.com/coder-labs/copilot/coder" version = "0.1.0" agent_id = coder_agent.example.id workdir = "/home/coder/projects" @@ -32,25 +32,25 @@ module "copilot_cli" { - **GitHub authentication** via one of: - [Coder external authentication](https://coder.com/docs/admin/external-auth) (recommended) - Direct token via `github_token` variable - - Interactive login in Copilot CLI + - Interactive login in Copilot ## Examples ### Usage with Tasks -For development environments where you want Copilot CLI to have full access to tools and automatically resume sessions: +For development environments where you want Copilot to have full access to tools and automatically resume sessions: ```tf data "coder_parameter" "ai_prompt" { type = "string" name = "AI Prompt" default = "" - description = "Initial task prompt for Copilot CLI." + description = "Initial task prompt for Copilot." mutable = true } -module "copilot_cli" { - source = "registry.coder.com/coder-labs/copilot-cli/coder" +module "copilot" { + source = "registry.coder.com/coder-labs/copilot/coder" version = "0.1.0" agent_id = coder_agent.example.id workdir = "/home/coder/projects" @@ -66,11 +66,11 @@ module "copilot_cli" { ### Advanced Configuration -Customize tool permissions, MCP servers, and Copilot CLI settings: +Customize tool permissions, MCP servers, and Copilot settings: ```tf -module "copilot_cli" { - source = "registry.coder.com/coder-labs/copilot-cli/coder" +module "copilot" { + source = "registry.coder.com/coder-labs/copilot/coder" version = "0.1.0" agent_id = coder_agent.example.id workdir = "/home/coder/projects" @@ -79,7 +79,7 @@ module "copilot_cli" { allow_tools = ["shell(git)", "shell(npm)", "write"] trusted_directories = ["/home/coder/projects", "/tmp"] - # Custom Copilot CLI configuration + # Custom Copilot configuration copilot_config = jsonencode({ banner = "never" theme = "dark" @@ -137,8 +137,8 @@ variable "github_token" { sensitive = true } -module "copilot_cli" { - source = "registry.coder.com/coder-labs/copilot-cli/coder" +module "copilot" { + source = "registry.coder.com/coder-labs/copilot/coder" version = "0.1.0" agent_id = coder_agent.example.id workdir = "/home/coder/projects" @@ -148,11 +148,11 @@ module "copilot_cli" { ### Standalone Mode -Run Copilot CLI as a command-line tool without task reporting or web interface. This installs and configures Copilot CLI, making it available as a CLI app in the Coder agent bar that you can launch to interact with Copilot CLI directly from your terminal. Set `report_tasks = false` to disable integration with Coder Tasks. +Run Copilot as a command-line tool without task reporting or web interface. This installs and configures Copilot, making it available as a CLI app in the Coder agent bar that you can launch to interact with Copilot directly from your terminal. Set `report_tasks = false` to disable integration with Coder Tasks. ```tf -module "copilot_cli" { - source = "registry.coder.com/coder-labs/copilot-cli/coder" +module "copilot" { + source = "registry.coder.com/coder-labs/copilot/coder" version = "0.1.0" agent_id = coder_agent.example.id workdir = "/home/coder" @@ -167,14 +167,14 @@ The module supports multiple authentication methods (in priority order): 1. **[Coder External Auth](https://coder.com/docs/admin/external-auth) (Recommended)** - Automatic if GitHub external auth is configured in Coder 2. **Direct Token** - Pass `github_token` variable (OAuth or Personal Access Token) -3. **Interactive** - Copilot CLI prompts for login via `/login` command if no auth found +3. **Interactive** - Copilot prompts for login via `/login` command if no auth found > [!NOTE] -> OAuth tokens work best with Copilot CLI. Personal Access Tokens may have limited functionality. +> OAuth tokens work best with Copilot. Personal Access Tokens may have limited functionality. ## Session Resumption -By default, the module resumes the latest Copilot CLI session when the workspace restarts. Set `resume_session = false` to always start fresh sessions. +By default, the module resumes the latest Copilot session when the workspace restarts. Set `resume_session = false` to always start fresh sessions. > [!NOTE] > Session resumption requires persistent storage for the home directory or workspace volume. Without persistent storage, sessions will not resume across workspace restarts. @@ -196,8 +196,8 @@ cat ~/.copilot-module/post_install.log ``` > [!NOTE] -> To use tasks with Copilot CLI, you must have an active GitHub Copilot subscription. -> The `workdir` variable is required and specifies the directory where Copilot CLI will run. +> To use tasks with Copilot, you must have an active GitHub Copilot subscription. +> The `workdir` variable is required and specifies the directory where Copilot will run. ## References diff --git a/registry/coder-labs/modules/copilot-cli/copilot-cli.tftest.hcl b/registry/coder-labs/modules/copilot/copilot.tftest.hcl similarity index 96% rename from registry/coder-labs/modules/copilot-cli/copilot-cli.tftest.hcl rename to registry/coder-labs/modules/copilot/copilot.tftest.hcl index a803ebbd7..185c019ba 100644 --- a/registry/coder-labs/modules/copilot-cli/copilot-cli.tftest.hcl +++ b/registry/coder-labs/modules/copilot/copilot.tftest.hcl @@ -32,8 +32,8 @@ run "defaults_are_correct" { } assert { - condition = resource.coder_env.mcp_app_status_slug.value == "copilot-cli" - error_message = "Status slug value should be 'copilot-cli'" + condition = resource.coder_env.mcp_app_status_slug.value == "copilot" + error_message = "Status slug value should be 'copilot'" } } @@ -225,8 +225,8 @@ run "app_slug_is_consistent" { } assert { - condition = local.app_slug == "copilot-cli" - error_message = "app_slug should be 'copilot-cli'" + condition = local.app_slug == "copilot" + error_message = "app_slug should be 'copilot'" } assert { diff --git a/registry/coder-labs/modules/copilot-cli/main.test.ts b/registry/coder-labs/modules/copilot/main.test.ts similarity index 97% rename from registry/coder-labs/modules/copilot-cli/main.test.ts rename to registry/coder-labs/modules/copilot/main.test.ts index 00d31104d..1d438e33b 100644 --- a/registry/coder-labs/modules/copilot-cli/main.test.ts +++ b/registry/coder-labs/modules/copilot/main.test.ts @@ -6,7 +6,7 @@ import { testRequiredVariables, } from "~test"; -describe("copilot-cli", async () => { +describe("copilot", async () => { await runTerraformInit(import.meta.dir); testRequiredVariables(import.meta.dir, { @@ -27,7 +27,7 @@ describe("copilot-cli", async () => { ); expect(statusSlugEnv).toBeDefined(); expect(statusSlugEnv.name).toBe("CODER_MCP_APP_STATUS_SLUG"); - expect(statusSlugEnv.value).toBe("copilot-cli"); + expect(statusSlugEnv.value).toBe("copilot"); }); it("creates github_token env var with correct value", async () => { diff --git a/registry/coder-labs/modules/copilot-cli/main.tf b/registry/coder-labs/modules/copilot/main.tf similarity index 92% rename from registry/coder-labs/modules/copilot-cli/main.tf rename to registry/coder-labs/modules/copilot/main.tf index 055d2571c..74f87ed55 100644 --- a/registry/coder-labs/modules/copilot-cli/main.tf +++ b/registry/coder-labs/modules/copilot/main.tf @@ -15,7 +15,7 @@ variable "agent_id" { variable "workdir" { type = string - description = "The folder to run Copilot CLI in." + description = "The folder to run Copilot in." } variable "external_auth_id" { @@ -43,7 +43,7 @@ variable "copilot_model" { variable "copilot_config" { type = string - description = "Custom Copilot CLI configuration as JSON string. Leave empty to use default configuration with banner disabled, theme set to auto, and workdir as trusted folder." + description = "Custom Copilot configuration as JSON string. Leave empty to use default configuration with banner disabled, theme set to auto, and workdir as trusted folder." default = "" } @@ -55,7 +55,7 @@ variable "ai_prompt" { variable "system_prompt" { type = string - description = "The system prompt to use for the Copilot CLI server." + description = "The system prompt to use for the Copilot server." default = <<-EOT You are a helpful AI assistant that helps with coding tasks. Always provide clear explanations and follow best practices. @@ -81,7 +81,7 @@ variable "system_prompt" { variable "trusted_directories" { type = list(string) - description = "Additional directories to trust for Copilot CLI operations." + description = "Additional directories to trust for Copilot operations." default = [] } @@ -118,7 +118,7 @@ variable "install_agentapi" { variable "agentapi_version" { type = string description = "The version of AgentAPI to install." - default = "v0.9.0" + default = "v0.10.0" } variable "report_tasks" { @@ -154,36 +154,36 @@ variable "icon" { variable "web_app_display_name" { type = string description = "Display name for the web app." - default = "Copilot CLI" + default = "Copilot" } variable "cli_app" { type = bool - description = "Whether to create a CLI app for Copilot CLI." + description = "Whether to create a CLI app for Copilot." default = false } variable "cli_app_display_name" { type = string description = "Display name for the CLI app." - default = "Copilot CLI" + default = "Copilot" } variable "resume_session" { type = bool - description = "Whether to automatically resume the latest Copilot CLI session on workspace restart." + description = "Whether to automatically resume the latest Copilot session on workspace restart." default = true } variable "pre_install_script" { type = string - description = "Custom script to run before configuring Copilot CLI." + description = "Custom script to run before configuring Copilot." default = null } variable "post_install_script" { type = string - description = "Custom script to run after configuring Copilot CLI." + description = "Custom script to run after configuring Copilot." default = null } @@ -192,7 +192,7 @@ data "coder_workspace_owner" "me" {} locals { workdir = trimsuffix(var.workdir, "/") - app_slug = "copilot-cli" + app_slug = "copilot" install_script = file("${path.module}/scripts/install.sh") start_script = file("${path.module}/scripts/start.sh") module_dir_name = ".copilot-module" diff --git a/registry/coder-labs/modules/copilot-cli/scripts/install.sh b/registry/coder-labs/modules/copilot/scripts/install.sh similarity index 91% rename from registry/coder-labs/modules/copilot-cli/scripts/install.sh rename to registry/coder-labs/modules/copilot/scripts/install.sh index f2cfa45b0..faff92744 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/install.sh +++ b/registry/coder-labs/modules/copilot/scripts/install.sh @@ -16,29 +16,29 @@ ARG_EXTERNAL_AUTH_ID=${ARG_EXTERNAL_AUTH_ID:-github} validate_prerequisites() { if ! command_exists node; then - echo "ERROR: Node.js not found. Copilot CLI requires Node.js v22+." + echo "ERROR: Node.js not found. Copilot requires Node.js v22+." echo "Install with: curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - && sudo apt-get install -y nodejs" exit 1 fi if ! command_exists npm; then - echo "ERROR: npm not found. Copilot CLI requires npm v10+." + echo "ERROR: npm not found. Copilot requires npm v10+." exit 1 fi node_version=$(node --version | sed 's/v//' | cut -d. -f1) if [ "$node_version" -lt 22 ]; then - echo "WARNING: Node.js v$node_version detected. Copilot CLI requires v22+." + echo "WARNING: Node.js v$node_version detected. Copilot requires v22+." fi } -install_copilot_cli() { +install_copilot() { if ! command_exists copilot; then echo "Installing GitHub Copilot CLI..." npm install -g @github/copilot if ! command_exists copilot; then - echo "ERROR: Failed to install Copilot CLI" + echo "ERROR: Failed to install Copilot" exit 1 fi @@ -69,7 +69,7 @@ check_github_authentication() { fi echo "⚠ No GitHub authentication detected" - echo " Copilot CLI will prompt for authentication when started" + echo " Copilot will prompt for authentication when started" echo " For seamless experience, configure GitHub external auth in Coder or run 'gh auth login'" return 0 } @@ -94,7 +94,7 @@ setup_copilot_config() { mkdir -p "$copilot_config_dir" if [ -n "$ARG_COPILOT_CONFIG" ]; then - echo "Setting up Copilot CLI configuration..." + echo "Setting up Copilot configuration..." if command_exists jq; then echo "$ARG_COPILOT_CONFIG" | jq 'del(.mcpServers)' > "$copilot_config_file" @@ -197,7 +197,7 @@ add_custom_mcp_servers() { configure_copilot_model() { if [ -n "$ARG_COPILOT_MODEL" ] && [ "$ARG_COPILOT_MODEL" != "claude-sonnet-4.5" ]; then - echo "Setting Copilot CLI model to: $ARG_COPILOT_MODEL" + echo "Setting Copilot model to: $ARG_COPILOT_MODEL" copilot config model "$ARG_COPILOT_MODEL" || { echo "WARNING: Failed to set model via copilot config, will use environment variable fallback" export COPILOT_MODEL="$ARG_COPILOT_MODEL" @@ -207,7 +207,7 @@ configure_copilot_model() { configure_coder_integration() { if [ "$ARG_REPORT_TASKS" = "true" ] && [ -n "$ARG_MCP_APP_STATUS_SLUG" ]; then - echo "Configuring Copilot CLI task reporting..." + echo "Configuring Copilot task reporting..." export CODER_MCP_APP_STATUS_SLUG="$ARG_MCP_APP_STATUS_SLUG" export CODER_MCP_AI_AGENTAPI_URL="http://localhost:3284" echo "✓ Coder MCP server configured for task reporting" @@ -219,10 +219,10 @@ configure_coder_integration() { } validate_prerequisites -install_copilot_cli +install_copilot check_github_authentication setup_copilot_configurations configure_copilot_model configure_coder_integration -echo "Copilot CLI module setup completed." +echo "Copilot module setup completed." diff --git a/registry/coder-labs/modules/copilot-cli/scripts/start.sh b/registry/coder-labs/modules/copilot/scripts/start.sh similarity index 92% rename from registry/coder-labs/modules/copilot-cli/scripts/start.sh rename to registry/coder-labs/modules/copilot/scripts/start.sh index 663d8998d..7a690e6c6 100644 --- a/registry/coder-labs/modules/copilot-cli/scripts/start.sh +++ b/registry/coder-labs/modules/copilot/scripts/start.sh @@ -21,7 +21,7 @@ ARG_RESUME_SESSION=${ARG_RESUME_SESSION:-true} validate_copilot_installation() { if ! command_exists copilot; then - echo "ERROR: Copilot CLI not installed. Run: npm install -g @github/copilot" + echo "ERROR: Copilot not installed. Run: npm install -g @github/copilot" exit 1 fi } @@ -81,7 +81,7 @@ check_existing_session() { session_id=$(basename "$latest_session_file" | sed 's/session_\(.*\)_[0-9]*.json/\1/') if [ -n "$session_id" ]; then - echo "Found existing Copilot CLI sessions. Will resume latest: $session_id" >&2 + echo "Found existing Copilot sessions. Will resume latest: $session_id" >&2 echo "$session_id" return 0 fi @@ -120,8 +120,8 @@ setup_github_authentication() { fi echo "⚠ No GitHub authentication available" - echo " Copilot CLI will prompt for login during first use" - echo " Use the '/login' command in Copilot CLI to authenticate" + echo " Copilot will prompt for login during first use" + echo " Use the '/login' command in Copilot to authenticate" return 0 } @@ -135,7 +135,7 @@ start_agentapi() { session_id=$(check_existing_session) if [ -n "$session_id" ]; then - echo "Resuming Copilot CLI session: $session_id" + echo "Resuming Copilot session: $session_id" if [ ${#COPILOT_ARGS[@]} -gt 0 ]; then echo "Copilot arguments: ${COPILOT_ARGS[*]}" agentapi server --type copilot --term-width 120 --term-height 40 -- copilot --resume "$session_id" "${COPILOT_ARGS[@]}" @@ -143,7 +143,7 @@ start_agentapi() { agentapi server --type copilot --term-width 120 --term-height 40 -- copilot --resume "$session_id" fi else - echo "Starting new Copilot CLI session..." + echo "Starting new Copilot session..." local initial_prompt initial_prompt=$(build_initial_prompt) diff --git a/registry/coder-labs/modules/copilot-cli/testdata/copilot-mock.sh b/registry/coder-labs/modules/copilot/testdata/copilot-mock.sh similarity index 75% rename from registry/coder-labs/modules/copilot-cli/testdata/copilot-mock.sh rename to registry/coder-labs/modules/copilot/testdata/copilot-mock.sh index cd7f6d24b..f1daa15f1 100644 --- a/registry/coder-labs/modules/copilot-cli/testdata/copilot-mock.sh +++ b/registry/coder-labs/modules/copilot/testdata/copilot-mock.sh @@ -7,6 +7,6 @@ if [[ "$1" == "--version" ]]; then fi while true; do - echo "$(date) - Copilot CLI mock running..." + echo "$(date) - Copilot mock running..." sleep 15 done From 5bceee2f5286714c8fd997a82293ea989ebf6761 Mon Sep 17 00:00:00 2001 From: DevCats Date: Mon, 6 Oct 2025 12:55:52 -0500 Subject: [PATCH 54/58] Update registry/coder-labs/modules/copilot/README.md Co-authored-by: Atif Ali --- registry/coder-labs/modules/copilot/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/coder-labs/modules/copilot/README.md b/registry/coder-labs/modules/copilot/README.md index f5ff477ec..f78506e55 100644 --- a/registry/coder-labs/modules/copilot/README.md +++ b/registry/coder-labs/modules/copilot/README.md @@ -20,7 +20,7 @@ module "copilot" { ``` > [!IMPORTANT] -> This example assumes you have [Coder external authentication](https://coder.com/docs/admin/external-auth) configured with `id = "github"`. If not, you can provide a direct token using the `github_token` variable. +> This example assumes you have [Coder external authentication](https://coder.com/docs/admin/external-auth) configured with `id = "github"`. If not, you can provide a direct token using the `github_token` variable or provide the correct external authentication id for GitHub by setting `external_auth_id = "my-github"`. > [!NOTE] > By default, this module is configured to run the embedded chat interface as a path-based application. In production, we recommend that you configure a [wildcard access URL](https://coder.com/docs/admin/setup#wildcard-access-url) and set `subdomain = true`. See [here](https://coder.com/docs/tutorials/best-practices/security-best-practices#disable-path-based-apps) for more details. From 158355799dc470ff55ca1217c7b32a622b514315 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Mon, 6 Oct 2025 17:52:22 -0500 Subject: [PATCH 55/58] feat: add copilot_version variable for version control in Copilot module and use new continue param for resuming sessions --- registry/coder-labs/modules/copilot/README.md | 3 +++ registry/coder-labs/modules/copilot/main.tf | 7 +++++ .../modules/copilot/scripts/install.sh | 9 +++++-- .../modules/copilot/scripts/start.sh | 27 +++++-------------- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/registry/coder-labs/modules/copilot/README.md b/registry/coder-labs/modules/copilot/README.md index f78506e55..c6e8dea26 100644 --- a/registry/coder-labs/modules/copilot/README.md +++ b/registry/coder-labs/modules/copilot/README.md @@ -75,6 +75,9 @@ module "copilot" { agent_id = coder_agent.example.id workdir = "/home/coder/projects" + # Version pinning (defaults to "0.0.334", use "latest" for newest version) + copilot_version = "latest" + # Tool permissions allow_tools = ["shell(git)", "shell(npm)", "write"] trusted_directories = ["/home/coder/projects", "/tmp"] diff --git a/registry/coder-labs/modules/copilot/main.tf b/registry/coder-labs/modules/copilot/main.tf index 74f87ed55..34cc5ad84 100644 --- a/registry/coder-labs/modules/copilot/main.tf +++ b/registry/coder-labs/modules/copilot/main.tf @@ -121,6 +121,12 @@ variable "agentapi_version" { default = "v0.10.0" } +variable "copilot_version" { + type = string + description = "The version of GitHub Copilot CLI to install. Use 'latest' for the latest version or specify a version like '0.0.334'." + default = "0.0.334" +} + variable "report_tasks" { type = bool description = "Whether to enable task reporting to Coder UI via AgentAPI." @@ -291,6 +297,7 @@ module "agentapi" { ARG_MCP_CONFIG='${var.mcp_config != "" ? base64encode(var.mcp_config) : ""}' \ ARG_COPILOT_CONFIG='${base64encode(local.final_copilot_config)}' \ ARG_EXTERNAL_AUTH_ID='${var.external_auth_id}' \ + ARG_COPILOT_VERSION='${var.copilot_version}' \ /tmp/install.sh EOT } \ No newline at end of file diff --git a/registry/coder-labs/modules/copilot/scripts/install.sh b/registry/coder-labs/modules/copilot/scripts/install.sh index faff92744..788efb41a 100644 --- a/registry/coder-labs/modules/copilot/scripts/install.sh +++ b/registry/coder-labs/modules/copilot/scripts/install.sh @@ -13,6 +13,7 @@ ARG_MCP_APP_STATUS_SLUG=${ARG_MCP_APP_STATUS_SLUG:-} ARG_MCP_CONFIG=$(echo -n "${ARG_MCP_CONFIG:-}" | base64 -d 2> /dev/null || echo "") ARG_COPILOT_CONFIG=$(echo -n "${ARG_COPILOT_CONFIG:-}" | base64 -d 2> /dev/null || echo "") ARG_EXTERNAL_AUTH_ID=${ARG_EXTERNAL_AUTH_ID:-github} +ARG_COPILOT_VERSION=${ARG_COPILOT_VERSION:-0.0.334} validate_prerequisites() { if ! command_exists node; then @@ -34,8 +35,12 @@ validate_prerequisites() { install_copilot() { if ! command_exists copilot; then - echo "Installing GitHub Copilot CLI..." - npm install -g @github/copilot + echo "Installing GitHub Copilot CLI (version: ${ARG_COPILOT_VERSION})..." + if [ "$ARG_COPILOT_VERSION" = "latest" ]; then + npm install -g @github/copilot + else + npm install -g "@github/copilot@${ARG_COPILOT_VERSION}" + fi if ! command_exists copilot; then echo "ERROR: Failed to install Copilot" diff --git a/registry/coder-labs/modules/copilot/scripts/start.sh b/registry/coder-labs/modules/copilot/scripts/start.sh index 7a690e6c6..91a404094 100644 --- a/registry/coder-labs/modules/copilot/scripts/start.sh +++ b/registry/coder-labs/modules/copilot/scripts/start.sh @@ -73,23 +73,11 @@ check_existing_session() { if copilot --help > /dev/null 2>&1; then local session_dir="$HOME/.copilot/history-session-state" if [ -d "$session_dir" ] && [ -n "$(ls "$session_dir"/session_*_*.json 2> /dev/null)" ]; then - local latest_session_file - latest_session_file=$(ls "$session_dir"/session_*_*.json 2> /dev/null | sort -t_ -k3 -n -r | head -n 1) - - if [ -n "$latest_session_file" ]; then - local session_id - session_id=$(basename "$latest_session_file" | sed 's/session_\(.*\)_[0-9]*.json/\1/') - - if [ -n "$session_id" ]; then - echo "Found existing Copilot sessions. Will resume latest: $session_id" >&2 - echo "$session_id" - return 0 - fi - fi + echo "Found existing Copilot session. Will continue latest session." >&2 + return 0 fi fi fi - echo "" return 1 } @@ -131,16 +119,13 @@ start_agentapi() { build_copilot_args - local session_id - session_id=$(check_existing_session) - - if [ -n "$session_id" ]; then - echo "Resuming Copilot session: $session_id" + if check_existing_session; then + echo "Continuing latest Copilot session..." if [ ${#COPILOT_ARGS[@]} -gt 0 ]; then echo "Copilot arguments: ${COPILOT_ARGS[*]}" - agentapi server --type copilot --term-width 120 --term-height 40 -- copilot --resume "$session_id" "${COPILOT_ARGS[@]}" + agentapi server --type copilot --term-width 120 --term-height 40 -- copilot --continue "${COPILOT_ARGS[@]}" else - agentapi server --type copilot --term-width 120 --term-height 40 -- copilot --resume "$session_id" + agentapi server --type copilot --term-width 120 --term-height 40 -- copilot --continue fi else echo "Starting new Copilot session..." From 172158f4f637de45a9004d334c8d9b0aea78a1f0 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Mon, 6 Oct 2025 18:18:01 -0500 Subject: [PATCH 56/58] refactor: change task reporting prompt to be injected after the system prompt so it can be changed without obstructing task reporting behaviour --- registry/coder-labs/modules/copilot/main.tf | 43 ++++++++++----------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/registry/coder-labs/modules/copilot/main.tf b/registry/coder-labs/modules/copilot/main.tf index 34cc5ad84..eaa92245d 100644 --- a/registry/coder-labs/modules/copilot/main.tf +++ b/registry/coder-labs/modules/copilot/main.tf @@ -55,28 +55,8 @@ variable "ai_prompt" { variable "system_prompt" { type = string - description = "The system prompt to use for the Copilot server." - default = <<-EOT - You are a helpful AI assistant that helps with coding tasks. Always provide clear explanations and follow best practices. - - -- Tool Selection -- - - coder_report_task: providing status updates or requesting user input. - - playwright: previewing your changes after you made them to confirm it worked as expected - - desktop-commander: use only for commands that keep running (servers, dev watchers, GUI apps). - - Built-in tools: use for everything else (file operations, git commands, builds & installs, one-off shell commands) - - Remember this decision rule: - - Stays running? → desktop-commander - - Finishes immediately? → built-in tools - - -- 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 + description = "The system prompt to use for the Copilot server. Task reporting instructions are automatically added when report_tasks is enabled." + 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 "trusted_directories" { @@ -221,6 +201,23 @@ locals { ) final_copilot_config = jsonencode(local.merged_copilot_config) + + 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 ? "${var.system_prompt}${local.task_reporting_prompt}" : var.system_prompt } resource "coder_env" "mcp_app_status_slug" { @@ -273,7 +270,7 @@ module "agentapi" { ARG_WORKDIR='${local.workdir}' \ ARG_AI_PROMPT='${base64encode(var.ai_prompt)}' \ - ARG_SYSTEM_PROMPT='${base64encode(var.system_prompt)}' \ + ARG_SYSTEM_PROMPT='${base64encode(local.final_system_prompt)}' \ ARG_COPILOT_MODEL='${var.copilot_model}' \ ARG_ALLOW_ALL_TOOLS='${var.allow_all_tools}' \ ARG_ALLOW_TOOLS='${join(",", var.allow_tools)}' \ From e28008d1b74c67d6aebf05009b6a71be23cde51b Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Mon, 6 Oct 2025 19:22:11 -0500 Subject: [PATCH 57/58] fix: update task reporting description in Copilot install script for better adherence to task reporting requirements --- registry/coder-labs/modules/copilot/scripts/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/coder-labs/modules/copilot/scripts/install.sh b/registry/coder-labs/modules/copilot/scripts/install.sh index 788efb41a..f44d50873 100644 --- a/registry/coder-labs/modules/copilot/scripts/install.sh +++ b/registry/coder-labs/modules/copilot/scripts/install.sh @@ -161,7 +161,7 @@ EOF "coder": { "command": "/tmp/coder-mcp-server.sh", "args": [], - "description": "Report ALL tasks and statuses (in progress, done, failed) you are working on.", + "description": "CRITICAL: Call this tool immediately after receiving ANY user message to report task status. Rules: (1) Call FIRST before doing work - report what you will do with state='working'. (2) Be granular - report each step separately. (3) State 'working' = actively processing without needing user input. (4) State 'complete' = task 100% finished. (5) State 'failure' = need user input, missing info, or blocked. Example: User says 'fix the bug' -> call with state='working', description='Investigating authentication bug'. When done -> call with state='complete', description='Fixed token validation'. You MUST report on every interaction.", "name": "Coder", "timeout": 3000, "type": "local", From 4fd0b395f8223ec8f135bebec78edfe201ea66b5 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Mon, 6 Oct 2025 19:52:36 -0500 Subject: [PATCH 58/58] refactor: update task reporting prompt formatting in Copilot module --- registry/coder-labs/modules/copilot/main.tf | 26 ++++++++++----------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/registry/coder-labs/modules/copilot/main.tf b/registry/coder-labs/modules/copilot/main.tf index eaa92245d..fd93b048f 100644 --- a/registry/coder-labs/modules/copilot/main.tf +++ b/registry/coder-labs/modules/copilot/main.tf @@ -203,21 +203,21 @@ locals { final_copilot_config = jsonencode(local.merged_copilot_config) 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 + +-- 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 ? "${var.system_prompt}${local.task_reporting_prompt}" : var.system_prompt + final_system_prompt = var.report_tasks ? "\n${var.system_prompt}${local.task_reporting_prompt}\n" : "\n${var.system_prompt}\n" } resource "coder_env" "mcp_app_status_slug" {