feat(dogfood/coder): integrate tasks support into "Write Coder on Coder" template (#19320)

Updates https://github.com/coder/internal/issues/836

- Adds an optional AI prompt parameter
- Conditionally adds the following resources if "AI Prompt" is provided:
  - `claude-code` module if AI prompt is provided
  - auto-restarting instance of `develop.sh` running in screen
  - a "preview" app that shows the local development server
This commit is contained in:
Cian Johnston
2025-08-15 17:47:31 +01:00
committed by GitHub
parent 6c902a7410
commit 2ea807fde1
3 changed files with 173 additions and 0 deletions
+1
View File
@@ -169,6 +169,7 @@ jobs:
CODER_URL: https://dev.coder.com
CODER_SESSION_TOKEN: ${{ secrets.CODER_SESSION_TOKEN }}
# Template source & details
TF_VAR_CODER_DOGFOOD_ANTHROPIC_API_KEY: ${{ secrets.CODER_DOGFOOD_ANTHROPIC_API_KEY }}
TF_VAR_CODER_TEMPLATE_NAME: ${{ secrets.CODER_TEMPLATE_NAME }}
TF_VAR_CODER_TEMPLATE_VERSION: ${{ steps.vars.outputs.sha_short }}
TF_VAR_CODER_TEMPLATE_DIR: ./coder
+159
View File
@@ -38,6 +38,7 @@ locals {
repo_base_dir = data.coder_parameter.repo_base_dir.value == "~" ? "/home/coder" : replace(data.coder_parameter.repo_base_dir.value, "/^~\\//", "/home/coder/")
repo_dir = replace(try(module.git-clone[0].repo_dir, ""), "/^~\\//", "/home/coder/")
container_name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}"
has_ai_prompt = data.coder_parameter.ai_prompt.value != ""
}
data "coder_workspace_preset" "cpt" {
@@ -150,6 +151,13 @@ data "coder_parameter" "image_type" {
}
}
variable "anthropic_api_key" {
type = string
description = "The API key used to authenticate with the Anthropic API."
default = ""
sensitive = true
}
locals {
default_regions = {
// keys should match group names
@@ -242,6 +250,14 @@ data "coder_parameter" "devcontainer_autostart" {
mutable = true
}
data "coder_parameter" "ai_prompt" {
type = "string"
name = "AI Prompt"
default = ""
description = "Prompt for Claude Code"
mutable = false
}
provider "docker" {
host = lookup(local.docker_host, data.coder_parameter.region.value)
}
@@ -380,6 +396,24 @@ module "devcontainers-cli" {
agent_id = coder_agent.dev.id
}
module "claude-code" {
count = local.has_ai_prompt ? data.coder_workspace.me.start_count : 0
source = "dev.registry.coder.com/coder/claude-code/coder"
version = "~>2.0"
agent_id = coder_agent.dev.id
folder = local.repo_dir
install_claude_code = true
claude_code_version = "latest"
order = 999
experiment_report_tasks = true
experiment_post_install_script = <<-EOT
claude mcp add playwright npx -- @playwright/mcp@latest --headless --isolated --no-sandbox
claude mcp add desktop-commander npx -- @wonderwhy-er/desktop-commander@latest
EOT
}
resource "coder_agent" "dev" {
arch = "amd64"
os = "linux"
@@ -710,4 +744,129 @@ resource "coder_metadata" "container_info" {
key = "region"
value = data.coder_parameter.region.option[index(data.coder_parameter.region.option.*.value, data.coder_parameter.region.value)].name
}
item {
key = "ai_task"
value = local.has_ai_prompt ? "yes" : "no"
}
}
resource "coder_env" "claude_system_prompt" {
count = local.has_ai_prompt ? data.coder_workspace.me.start_count : 0
agent_id = coder_agent.dev.id
name = "CODER_MCP_CLAUDE_SYSTEM_PROMPT"
value = <<-EOT
<system>
-- Framing --
You are a helpful Coding assistant. Aim to autonomously investigate
and solve issues the user gives you and test your work, whenever possible.
Avoid shortcuts like mocking tests. When you get stuck, you can ask the user
but opt for autonomy.
-- 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. IMMEDIATELY report status after receiving ANY user message
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
In your summary:
- Be specific about what you're doing
- Clearly indicate what information you need from the user when in
"failure" state
- Keep it under 160 characters
- Make it actionable
-- Context --
There is an existing application in the current directory.
Be sure to read CLAUDE.md before making any changes.
This is a real-world production application. As such, make sure to think carefully, use TODO lists, and plan carefully before making changes.
</system>
EOT
}
resource "coder_env" "claude_task_prompt" {
count = local.has_ai_prompt ? data.coder_workspace.me.start_count : 0
agent_id = coder_agent.dev.id
name = "CODER_MCP_CLAUDE_TASK_PROMPT"
value = data.coder_parameter.ai_prompt.value
}
resource "coder_env" "anthropic_api_key" {
count = local.has_ai_prompt ? data.coder_workspace.me.start_count : 0
agent_id = coder_agent.dev.id
name = "ANTHROPIC_API_KEY"
value = var.anthropic_api_key
}
resource "coder_app" "develop_sh" {
count = local.has_ai_prompt ? data.coder_workspace.me.start_count : 0
agent_id = coder_agent.dev.id
slug = "develop-sh"
display_name = "develop.sh"
icon = "${data.coder_workspace.me.access_url}/emojis/1f4bb.png" // 💻
command = "screen -x develop_sh"
share = "authenticated"
subdomain = true
open_in = "tab"
order = 0
}
resource "coder_script" "develop_sh" {
count = local.has_ai_prompt ? data.coder_workspace.me.start_count : 0
display_name = "develop.sh"
agent_id = coder_agent.dev.id
run_on_start = true
start_blocks_login = false
icon = "${data.coder_workspace.me.access_url}/emojis/1f4bb.png" // 💻
script = <<-EOT
#!/usr/bin/env bash
set -eux -o pipefail
# Wait for the agent startup script to finish.
for attempt in {1..60}; do
if [[ -f /tmp/.coder-startup-script.done ]]; then
break
fi
echo "Waiting for agent startup script to finish... ($attempt/60)"
sleep 10
done
cd "${local.repo_dir}" && screen -dmS develop_sh /bin/sh -c 'while true; do ./scripts/develop.sh --; echo "develop.sh exited with code $? restarting in 30s"; sleep 30; done'
EOT
}
resource "coder_app" "preview" {
count = local.has_ai_prompt ? data.coder_workspace.me.start_count : 0
agent_id = coder_agent.dev.id
slug = "preview"
display_name = "Preview"
icon = "${data.coder_workspace.me.access_url}/emojis/1f50e.png" // 🔎
url = "http://localhost:8080"
share = "authenticated"
subdomain = true
open_in = "tab"
order = 1
healthcheck {
url = "http://localhost:8080/healthz"
interval = 5
threshold = 15
}
}
+13
View File
@@ -33,6 +33,13 @@ variable "CODER_TEMPLATE_MESSAGE" {
type = string
}
variable "CODER_DOGFOOD_ANTHROPIC_API_KEY" {
type = string
description = "The API key that workspaces will use to authenticate with the Anthropic API."
default = ""
sensitive = true
}
resource "coderd_template" "dogfood" {
name = var.CODER_TEMPLATE_NAME
display_name = "Write Coder on Coder"
@@ -45,6 +52,12 @@ resource "coderd_template" "dogfood" {
message = var.CODER_TEMPLATE_MESSAGE
directory = var.CODER_TEMPLATE_DIR
active = true
tf_vars = [
{
name = "anthropic_api_key"
value = var.CODER_DOGFOOD_ANTHROPIC_API_KEY
}
]
}
]
acl = {