feat: sharkymark profile and claude docker template (#304)

Closes #

## Description

registry profile creation and template submission for claude on docker

## Type of Change

- [x] New template
- [ ] Bug fix
- [ ] Feature/enhancement
- [ ] Documentation
- [ ] Other

## Template Information

**Path:** `registry/sharkymark/templates/docker-claude`  
**New version:** `v1.0.0`  
**Breaking change:** [ ] Yes [x] No

## Testing & Validation

- [ ] Tests pass (`bun test`)
- [ ] Code formatted (`bun run fmt`)
- [x] Changes tested locally

## Related Issues

n/a

---------

Co-authored-by: DevCats <christofer@coder.com>
This commit is contained in:
sharkymark 2025-08-09 09:40:04 -07:00 committed by GitHub
parent 673caf2e95
commit 59b0472125
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 429 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -0,0 +1,32 @@
---
display_name: "Mark Milligan"
bio: "VP of Revenue at https://nuon.co. Former VP of Sales at Coder. Love building startup revenue teams and tinkering with technology."
avatar_url: "./.images/avatar.png"
github: "sharkymark"
linkedin: "https://www.linkedin.com/in/marktmilligan" # Optional
website: "https://markmilligan.io" # Optional
support_email: "mtm20176@gmail.com" # Optional
status: "community"
---
# Mark Milligan
Former VP of Sales at Coder for 4 years, and now VP of Revenue at Nuon. I love building startup revenue teams and tinkering with technology.
## About Me
Visit my [website](https://markmilligan.io) to learn more about my work and interests.
## Links
[My presentation about Great White Sharks](https://docs.google.com/presentation/d/13I3Af7l-ZSVCh-ovEvOKIM30ABIvNKhkRC3CnYZN450/edit?slide=id.p#slide=id.p) - given twice in 2020 and 2021 to the Coder team.
[NOAA Radar](https://radar.weather.gov/)
[Flight Radar](https://www.flightradar24.com/airport/aus)
### Webcams
[Austin - facing south](https://cctv.austinmobility.io/image/51.jpg)
[Austin - facing north](https://cctv.austinmobility.io/image/52.jpg)

View File

@ -0,0 +1,34 @@
---
display_name: "Claude Code AI Agent Template"
description: The goal is to try the experimental ai agent integration with Claude CodeAI agent
icon: "../../../../.icons/claude.svg"
verified: false
tags: ["ai", "docker", "container", "claude", "agent", "tasks"]
---
# ai agent template for a workspace in a container on a Docker host
### Docker image
1. Based on Coder-managed image `codercom/example-universal:ubuntu`
[Image on DockerHub](https://hub.docker.com/r/codercom/example-universal)
### Apps included
1. A web-based terminal
1. code-server Web IDE
1. A [sample app](https://github.com/gothinkster/realworld) to test the environment
1. [Claude Code AI agent](https://www.anthropic.com/claude-code) to assist with development tasks
### Resources
[Coder docs on AI agents and tasks](https://coder.com/docs/ai-coder/tasks)
[main.tf for Coder example](https://github.com/coder/registry/blob/main/registry/coder-labs/templates/tasks-docker/main.tf)
[Claude Code Coder Terraform module](https://registry.coder.com/modules/coder/claude-code)
[Docker Terraform provider](https://registry.terraform.io/providers/kreuzwerker/docker/latest/docs)
[Coder Terraform provider](https://registry.terraform.io/providers/coder/coder/latest/docs)

View File

@ -0,0 +1,363 @@
terraform {
required_providers {
coder = {
source = "coder/coder"
}
docker = {
source = "kreuzwerker/docker"
}
}
}
provider "docker" {
host = var.socket
}
provider "coder" {
}
data "coder_workspace" "me" {
}
data "coder_workspace_owner" "me" {
}
data "coder_provisioner" "me" {
}
variable "socket" {
type = string
description = <<-EOF
The Unix socket that the Docker daemon listens on and how containers
communicate with the Docker daemon.
Either Unix or TCP
e.g., unix:///var/run/docker.sock
EOF
default = "unix:///var/run/docker.sock"
}
variable "anthropic_api_key" {
type = string
description = "Generate one at: https://console.anthropic.com/settings/keys"
sensitive = true
}
resource "coder_env" "anthropic_api_key" {
agent_id = coder_agent.dev.id
name = "CODER_MCP_CLAUDE_API_KEY"
value = var.anthropic_api_key
}
# The Claude Code module does the automatic task reporting
# Other agent modules: https://registry.coder.com/modules?search=agent
# Or use a custom agent:
module "claude-code" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/claude-code/coder"
version = "2.0.0"
agent_id = coder_agent.dev.id
folder = "/home/coder/projects"
install_claude_code = true
claude_code_version = "latest"
order = 999
experiment_post_install_script = data.coder_parameter.setup_script.value
# This enables Coder Tasks
experiment_report_tasks = true
}
# We are using presets to set the prompts, image, and set up instructions
# See https://coder.com/docs/admin/templates/extending-templates/parameters#workspace-presets
data "coder_workspace_preset" "default" {
name = "Real World App: Angular + Django"
default = true
parameters = {
"system_prompt" = <<-EOT
-- Framing --
You are a helpful assistant that can help with code. You are running inside a Coder Workspace and provide status updates to the user via Coder MCP. Stay on track, feel free to debug, but when the original plan fails, do not choose a different route/architecture without checking the user first.
-- Tool Selection --
- 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
-- Context --
There is an existing app and tmux dev server running on port 8000. Be sure to read it's CLAUDE.md (./realworld-django-rest-framework-angular/CLAUDE.md) to learn more about it.
Since this app is for demo purposes and the user is previewing the homepage and subsequent pages, aim to make the first visual change/prototype very quickly so the user can preview it, then focus on backend or logic which can be a more involved, long-running architecture plan.
EOT
"setup_script" = <<-EOT
# Set up projects dir
mkdir -p /home/coder/projects
cd $HOME/projects
# Packages: Install additional packages
sudo apt-get update && sudo apt-get install -y tmux
if ! command -v google-chrome >/dev/null 2>&1; then
yes | npx playwright install chrome
fi
# MCP: Install and configure MCP Servers
npm install -g @wonderwhy-er/desktop-commander
claude mcp add playwright npx -- @playwright/mcp@latest --headless --isolated --no-sandbox
claude mcp add desktop-commander desktop-commander
# Repo: Clone and pull changes from the git repository
if [ ! -d "realworld-django-rest-framework-angular" ]; then
git clone https://github.com/coder-contrib/realworld-django-rest-framework-angular.git
else
cd realworld-django-rest-framework-angular
git fetch
# Check for uncommitted changes
if git diff-index --quiet HEAD -- && \
[ -z "$(git status --porcelain --untracked-files=no)" ] && \
[ -z "$(git log --branches --not --remotes)" ]; then
echo "Repo is clean. Pulling latest changes..."
git pull
else
echo "Repo has uncommitted or unpushed changes. Skipping pull."
fi
cd ..
fi
# Initialize: Start the development server
cd realworld-django-rest-framework-angular && ./start-dev.sh
EOT
"preview_port" = "4200"
"container_image" = "codercom/example-universal:ubuntu"
}
}
# Advanced parameters (these are all set via preset)
data "coder_parameter" "system_prompt" {
name = "system_prompt"
display_name = "System Prompt"
type = "string"
form_type = "textarea"
description = "System prompt for the agent with generalized instructions"
mutable = false
}
data "coder_parameter" "ai_prompt" {
type = "string"
name = "AI Prompt"
default = ""
description = "Write a prompt for Claude Code"
mutable = true
}
data "coder_parameter" "setup_script" {
name = "setup_script"
display_name = "Setup Script"
type = "string"
form_type = "textarea"
description = "Script to run before running the agent"
mutable = false
}
data "coder_parameter" "container_image" {
name = "container_image"
display_name = "Container Image"
type = "string"
default = "codercom/example-universal:ubuntu"
mutable = false
}
data "coder_parameter" "preview_port" {
name = "preview_port"
display_name = "Preview Port"
description = "The port the web app is running to preview in Tasks"
type = "number"
default = "3000"
mutable = false
}
# Other variables for Claude Code
resource "coder_env" "claude_task_prompt" {
agent_id = coder_agent.dev.id
name = "CODER_MCP_CLAUDE_TASK_PROMPT"
value = data.coder_parameter.ai_prompt.value
}
resource "coder_env" "app_status_slug" {
agent_id = coder_agent.dev.id
name = "CODER_MCP_APP_STATUS_SLUG"
value = "claude-code"
}
resource "coder_env" "claude_system_prompt" {
agent_id = coder_agent.dev.id
name = "CODER_MCP_CLAUDE_SYSTEM_PROMPT"
value = data.coder_parameter.system_prompt.value
}
module "coder-login" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/coder-login/coder"
agent_id = coder_agent.dev.id
}
module "dotfiles" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/dotfiles/coder"
agent_id = coder_agent.dev.id
}
module "code-server" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/code-server/coder"
agent_id = coder_agent.dev.id
folder = "/home/coder/projects"
}
module "git-config" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/git-config/coder"
agent_id = coder_agent.dev.id
}
resource "coder_agent" "dev" {
arch = data.coder_provisioner.me.arch
os = "linux"
# The following metadata blocks are optional. They are used to display
# information about your workspace in the dashboard. You can remove them
# if you don't want to display any information.
# For basic resources, you can use the `coder stat` command.
# If you need more control, you can write your own script.
metadata {
display_name = "CPU Usage"
key = "0_cpu_usage"
script = "coder stat cpu"
interval = 10
timeout = 1
}
metadata {
display_name = "RAM Usage"
key = "1_ram_usage"
script = "coder stat mem"
interval = 10
timeout = 1
}
metadata {
display_name = "Home Disk"
key = "3_home_disk"
script = "coder stat disk --path $${HOME}"
interval = 60
timeout = 1
}
metadata {
display_name = "CPU Usage (Host)"
key = "4_cpu_usage_host"
script = "coder stat cpu --host"
interval = 10
timeout = 1
}
metadata {
display_name = "Memory Usage (Host)"
key = "5_mem_usage_host"
script = "coder stat mem --host"
interval = 10
timeout = 1
}
display_apps {
vscode = true
vscode_insiders = false
ssh_helper = false
port_forwarding_helper = true
web_terminal = true
}
startup_script_behavior = "non-blocking"
connection_timeout = 300
env = {
GIT_AUTHOR_NAME = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name)
GIT_AUTHOR_EMAIL = "${data.coder_workspace_owner.me.email}"
GIT_COMMITTER_NAME = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name)
GIT_COMMITTER_EMAIL = "${data.coder_workspace_owner.me.email}"
}
startup_script = <<EOT
#!/bin/sh
EOT
}
resource "coder_app" "preview" {
agent_id = coder_agent.dev.id
slug = "preview"
display_name = "Preview your app"
icon = "${data.coder_workspace.me.access_url}/emojis/1f50e.png"
url = "http://localhost:${data.coder_parameter.preview_port.value}"
share = "authenticated"
subdomain = true
open_in = "tab"
order = 0
healthcheck {
url = "http://localhost:${data.coder_parameter.preview_port.value}/"
interval = 5
threshold = 15
}
}
resource "docker_container" "workspace" {
count = data.coder_workspace.me.start_count
image = data.coder_parameter.container_image.value
# Uses lower() to avoid Docker restriction on container names.
name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}"
hostname = lower(data.coder_workspace.me.name)
dns = ["1.1.1.1"]
# Use the docker gateway if the access URL is 127.0.0.1
#entrypoint = ["sh", "-c", replace(coder_agent.dev.init_script, "127.0.0.1", "host.docker.internal")]
# Use the docker gateway if the access URL is 127.0.0.1
command = [
"sh", "-c",
<<EOT
trap '[ $? -ne 0 ] && echo === Agent script exited with non-zero code. Sleeping infinitely to preserve logs... && sleep infinity' EXIT
${replace(coder_agent.dev.init_script, "/localhost|127\\.0\\.0\\.1/", "host.docker.internal")}
EOT
]
env = ["CODER_AGENT_TOKEN=${coder_agent.dev.token}"]
volumes {
container_path = "/home/coder/"
volume_name = docker_volume.coder_volume.name
read_only = false
}
host {
host = "host.docker.internal"
ip = "host-gateway"
}
}
resource "docker_volume" "coder_volume" {
name = "coder-${data.coder_workspace_owner.me.name}-${data.coder_workspace.me.name}"
}
resource "coder_metadata" "workspace_info" {
count = data.coder_workspace.me.start_count
resource_id = docker_container.workspace[0].id
item {
key = "image"
value = data.coder_parameter.container_image.value
}
}