mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
Reference in New Issue
Block a user