From a84c1ac23d8b9989208ef09c2512e9725526c079 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Wed, 25 Mar 2026 15:16:59 -0500 Subject: [PATCH 01/11] feat: add coder-modules and coder-templates skills --- .claude/skills/coder-modules/SKILL.md | 398 ++++++++++++++++++++++++ .claude/skills/coder-templates/SKILL.md | 393 +++++++++++++++++++++++ 2 files changed, 791 insertions(+) create mode 100644 .claude/skills/coder-modules/SKILL.md create mode 100644 .claude/skills/coder-templates/SKILL.md diff --git a/.claude/skills/coder-modules/SKILL.md b/.claude/skills/coder-modules/SKILL.md new file mode 100644 index 000000000..705c2a001 --- /dev/null +++ b/.claude/skills/coder-modules/SKILL.md @@ -0,0 +1,398 @@ +--- +name: coder-modules +description: Creates and updates Coder Registry modules with proper scaffolding, Terraform testing, README frontmatter, and version management +--- + +# Coder Modules + +Coder Registry modules are reusable Terraform components that live under `registry//modules//` and are consumed by templates via `module` blocks. + +## Before You Start + +Before writing or modifying any code: + +1. **Understand the request.** What tool, integration, or functionality is the module providing? What Coder resources does it need (`coder_script`, `coder_app`, `coder_env`, etc.)? Read the official documentation for the target tool or integration (installation steps, CLI flags, config files, environment variables, ports) so you can implement the module properly without guessing. +2. **Research existing modules.** Search the registry for similar modules. Read their `main.tf` to understand patterns, variable conventions, and how they solve similar problems. Avoid duplicating existing functionality. +3. **Check the Coder provider docs.** Verify that the resources and attributes you plan to use exist in the provider version you're targeting. Use the version-specific docs URL if needed. +4. **Clarify before building.** If the request is ambiguous (e.g. unclear which Coder resource to use, whether a `coder_app` vs `coder_script` is appropriate, what variables to expose, or which namespace to use), ask for clarification rather than guessing. Never assume a namespace; always confirm with the user. +5. **Plan the structure.** Decide on script organization (root `run.sh`, `scripts/` directory, or inline), what variables to expose, and what tests to write. + +## Documentation References + +### Coder + +- Coder docs (latest): +- Version-specific Coder docs: `https://coder.com/docs/@v{MAJOR}.{MINOR}.{PATCH}` (e.g. ) +- Coder Registry: + +### Coder Terraform provider + +- Provider docs (latest): +- Version-specific provider docs: replace `latest` with a version number (e.g. ) + +Resources: + +| Resource | Docs | +| ---------------- | ------------------------------------------------------------------------------------ | +| `coder_app` | | +| `coder_script` | | +| `coder_env` | | +| `coder_metadata` | | + +Data sources: + +| Data Source | Docs | +| ----------------------- | ---------------------------------------------------------------------------------------------- | +| `coder_parameter` | | +| `coder_workspace` | | +| `coder_workspace_owner` | | + +## Scaffolding a New Module + +Only use this when creating a brand new module that does not yet exist. When updating an existing module, edit its files directly. + +From repo root: + +```bash +./scripts/new_module.sh namespace/module-name +``` + +Creates `registry//modules//` with: + +- `main.tf`: Terraform config with coder provider +- `README.md`: frontmatter and usage examples +- `MODULE_NAME.tftest.hcl`: Terraform native tests +- `run.sh`: install/start-up script template + +If the namespace is new, the script also creates `registry//` with a README. New namespaces additionally need: + +- `registry//.images/avatar.png` (or `.svg`): square image, 400x400px minimum +- The namespace README `avatar` field pointing to `./.images/avatar.png` + +The generated namespace README contains placeholder fields (name, links, avatar) that the user must fill out. After completing the module, inform the user that the namespace README needs to be updated with their information. + +## main.tf + +```tf +terraform { + required_version = ">= 1.0" + + required_providers { + coder = { + source = "coder/coder" + version = ">= 2.5" + } + } +} + +variable "agent_id" { + type = string + description = "The ID of a Coder agent." +} + +resource "coder_script" "my_tool" { + agent_id = var.agent_id + display_name = "My Tool" + icon = local.icon_url + script = templatefile("${path.module}/run.sh", { + LOG_PATH : var.log_path, + }) + run_on_start = true + run_on_stop = false +} + +resource "coder_app" "my_tool" { + agent_id = var.agent_id + slug = "my-tool" + display_name = "My Tool" + url = "http://localhost:${var.port}" + icon = local.icon_url + subdomain = false + share = "owner" + order = var.order + + healthcheck { + url = "http://localhost:${var.port}/healthz" + interval = 5 + threshold = 6 + } +} +``` + +Key patterns: + +- Provider version constraints must reflect actual functionality requirements. Only raise the minimum `coder` provider version (e.g. `>= 2.5` to `>= 2.8`) when the module uses a resource, attribute, or behavior introduced in that version; check the provider changelog to confirm. +- Variable names MUST be `snake_case` (no hyphens; validation rejects them) +- New variables must have sensible defaults for backward compatibility +- Common variable: `agent_id` (string, required, no default) +- Common variable: `order` (number, default `null`, controls UI position) +- Use `locals {}` for computed values: URL normalization, base64 encoding, `file()` script content, config assembly +- Modules can consume other registry modules via `module` blocks (e.g. `cursor` uses `vscode-desktop-core`, CLI wrappers use `agentapi`) +- `coder_script` icons use the `/icon/.svg` format. The `display_name` is typically the product name (e.g. "code-server", "Git Clone", "File Browser"). +- Do not add comments that narrate what the code does or label sections. Only comment when explaining something non-obvious (e.g. why a workaround exists, a subtle constraint, or an unusual design choice). + +## README.md + +Required YAML frontmatter: + +```yaml +--- +display_name: My Tool +description: Short description of what this module does +icon: ../../../../.icons/tool.svg +verified: false +tags: [helper, ide] +--- +``` + +Content rules: + +- Single H1 heading matching `display_name`, directly below frontmatter +- When increasing header levels, increment by one each time (h1 -> h2 -> h3, not h1 -> h3) +- Usage snippet with `registry.coder.com///coder` and pinned `version` +- Code fences labeled `tf` (NOT `hcl`) +- Relative icon paths (e.g. `../../../../.icons/`) +- **Do NOT include tables or lists that enumerate variables, parameters, or outputs.** The registry generates variable and output documentation automatically from the Terraform source. Describe what the module does and how to use it in prose, not by listing every configurable field. +- Usage examples are encouraged +- Use [GFM alerts](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts) for callouts: `> [!NOTE]`, `> [!TIP]`, `> [!IMPORTANT]`, `> [!WARNING]`, `> [!CAUTION]` + +```tf +module "my_tool" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/namespace/my-tool/coder" + version = "1.0.0" + agent_id = coder_agent.main.id +} +``` + +## Icons + +Modules reference icons in two places with different path systems: + +- **README frontmatter** `icon:` uses a relative path to the repo's `.icons/` directory (e.g. `../../../../.icons/my-tool.svg`). Displayed on the registry website. +- **`coder_script` / `coder_app`** `icon =` uses an absolute `/icon/.svg` path served by the Coder deployment from `site/static/icon/` in the `coder/coder` repo. Displayed in the workspace agent bar. + +Workflow: + +1. **Check what exists.** List the `.icons/` directory at the repo root for available SVGs. For `/icon/` paths, look at what similar modules already use. +2. **Use existing icons when they fit.** If the tool already has an icon in `.icons/` and `/icon/`, use those. +3. **When an icon doesn't exist,** reference the expected path anyway (e.g. `../../../../.icons/my-tool.svg` and `/icon/my-tool.svg`) so the structure is correct. Try to source the official SVG from the tool's branding page or repository. If you can obtain it, add it to `.icons/` in this repo. +4. **Don't substitute a generic icon.** If the tool has its own brand identity, use the correct name even if the file doesn't exist yet. Don't fall back to generic icons like `coder.svg` or `terminal.svg`. +5. **Notify the user.** After completing the module, inform the user in your response if any icons were referenced but not found. Note that missing icons need to be sourced and added to both this repo's `.icons/` directory and the `coder/coder` repo at `site/static/icon/`. + +## Scripts + +Modules use three patterns for shell logic, depending on complexity: + +### Root `run.sh` + `templatefile()` (simple modules) + +A single `run.sh` at the module root, loaded via `templatefile()` to inject Terraform variables. Used by `code-server`, `vscode-web`, `git-clone`, `dotfiles`, `filebrowser`. + +```tf +resource "coder_script" "my_tool" { + agent_id = var.agent_id + display_name = "My Tool" + icon = "/icon/my-tool.svg" + script = templatefile("${path.module}/run.sh", { + LOG_PATH : var.log_path, + }) + run_on_start = true +} +``` + +Use `$${VAR}` (double dollar) in the shell script for Terraform `templatefile` escaping. + +### `scripts/` directory + `file()` (complex modules) + +Separate `scripts/install.sh` and `scripts/start.sh` loaded via `file()` into `locals`, then passed to a child module or encoded inline. Used by `claude-code`, `copilot`, `codex`, `cursor-cli`, `amazon-q`. + +```tf +locals { + install_script = file("${path.module}/scripts/install.sh") + start_script = file("${path.module}/scripts/start.sh") +} +``` + +Use `file()` when scripts don't need Terraform variable interpolation. For config templates, use a `templates/` directory with `templatefile()` (e.g. `amazon-q/templates/agent-config.json.tpl`). + +### Inline heredoc (minimal modules) + +For trivial logic, embed the script directly in the `coder_script` resource. Used by `cursor`, `zed`. + +Modules that use a `scripts/` directory often also have a `testdata/` directory containing mock scripts for testing (e.g. `testdata/my-tool-mock.sh`). + +## Testing + +### .tftest.hcl (Required) + +Every module must have Terraform native tests. The file can be named `main.tftest.hcl` or `.tftest.hcl`. Use `command = plan` for most cases: + +```hcl +run "plan_with_defaults" { + command = plan + + variables { + agent_id = "test-agent-id" + } + + assert { + condition = var.agent_id == "test-agent-id" + error_message = "agent_id should be set" + } +} + +run "custom_port" { + command = plan + + variables { + agent_id = "test-agent-id" + port = 8080 + } + + assert { + condition = resource.coder_app.my_tool.url == "http://localhost:8080" + error_message = "App URL should use configured port" + } +} +``` + +Advanced patterns: + +- `override_data` to mock data sources like `coder_workspace` and `coder_workspace_owner` +- `command = apply` when testing outputs or computed values +- `expect_failures` to test validation rules +- `regexall()` / `startswith()` / `endswith()` for string assertions +- Assert on `coder_env`, `coder_script`, `coder_app` resource attributes + +```hcl +run "with_mocked_workspace" { + command = apply + + variables { + agent_id = "foo" + } + + override_data { + target = data.coder_workspace.me + values = { + name = "test-workspace" + } + } + + assert { + condition = output.url == "expected-value" + error_message = "URL should match expected format" + } +} + +run "validation_rejects_conflict" { + command = plan + + variables { + agent_id = "test" + option_a = true + option_b = true + } + + expect_failures = [ + var.option_a, + ] +} +``` + +### main.test.ts (Optional) + +For more complex testing (Docker containers, script execution, HTTP mocking). +Import from `~test` (mapped to `test/test.ts` via `tsconfig.json`): + +```typescript +import { describe, expect, it } from "bun:test"; +import { + runTerraformApply, + runTerraformInit, + testRequiredVariables, + findResourceInstance, +} from "~test"; + +describe("my-tool", () => { + it("should init successfully", async () => { + await runTerraformInit(import.meta.dir); + }); + + testRequiredVariables(import.meta.dir, { + agent_id: "test-agent", + }); + + it("should apply with defaults", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "test-agent", + }); + const app = findResourceInstance(state, "coder_app"); + expect(app.slug).toBe("my-tool"); + expect(app.display_name).toBe("My Tool"); + }); +}); +``` + +### Test utility API (`~test`) + +**Terraform helpers:** + +- `runTerraformInit(dir)`: runs `terraform init`. +- `runTerraformApply(dir, vars, customEnv?)`: runs `terraform apply` with a random state file and returns `TerraformState`. Variables are passed as `TF_VAR_*`. Safe to run in parallel. +- `testRequiredVariables(dir, vars)`: auto-generates test cases (one success with all vars, plus one per var verifying apply fails without it). Pass `{}` if there are no required vars. +- `findResourceInstance(state, type, name?)`: finds the first resource instance by type. Throws if not found. Optionally filters by name. + +**Docker helpers** (require `--network host`, Linux/Colima/OrbStack): + +- `runContainer(image, init?)`: starts a detached container and returns its ID. Labeled `modules-test=true` for auto-cleanup. +- `removeContainer(id)`: force-removes a container. +- `execContainer(id, cmd, args?)`: runs a command in a container and returns `{ exitCode, stdout, stderr }`. +- `executeScriptInContainer(state, image, shell?, before?)`: finds `coder_script` in state, runs it in a container, and returns `{ exitCode, stdout: string[], stderr: string[] }`. + +**File helpers:** + +- `writeCoder(id, script)`: writes a mock `coder` CLI to `/usr/bin/coder` in the container. +- `writeFileContainer(id, path, content, { user? })`: writes a file to the container via base64. +- `readFileContainer(id, path)`: reads a file from the container as root. + +**HTTP helpers:** + +- `createJSONResponse(obj, statusCode?)`: creates a `Response` with a JSON body (defaults to 200). + +Cleanup of `*.tfstate` files and `modules-test` Docker containers is handled automatically by `setup.ts` (preloaded via `bunfig.toml`). + +## Commands + +| Task | Command | Scope | +| ---------------- | ----------------------------------------------------- | ---------- | +| Format all | `bun run fmt` | Repo | +| Terraform tests | `bun run tftest` | Repo | +| TypeScript tests | `bun run tstest` | Repo | +| Single TF test | `terraform init -upgrade && terraform test -verbose` | Module dir | +| Single TS test | `bun test main.test.ts` | Module dir | +| Validate | `./scripts/terraform_validate.sh` | Repo | +| Version bump | `.github/scripts/version-bump.sh patch\|minor\|major` | Module dir | + +Always run `bun run fmt` before committing. + +## Version Management + +Bump version via `.github/scripts/version-bump.sh` when modifying modules: + +- `patch`: bugfixes +- `minor`: new features, new variables with defaults +- `major`: breaking changes (removed inputs, changed defaults, new required variables) + +The script automatically updates `version` references in README usage examples. + +## PR Checklist + +- [ ] Version bumped if module changed (script also updates README examples) +- [ ] Breaking changes documented (removed inputs, changed defaults, new required variables) +- [ ] New variables have sensible defaults +- [ ] Tests pass: `bun run tftest` and `bun run tstest` +- [ ] Shell scripts handle errors gracefully (`|| echo "Warning..."` for non-fatal) +- [ ] No hardcoded values that should be configurable +- [ ] No absolute URLs (use relative paths) +- [ ] AI attribution in PR footer (e.g. "Generated with Claude") diff --git a/.claude/skills/coder-templates/SKILL.md b/.claude/skills/coder-templates/SKILL.md new file mode 100644 index 000000000..4c7ced8ee --- /dev/null +++ b/.claude/skills/coder-templates/SKILL.md @@ -0,0 +1,393 @@ +--- +name: coder-templates +description: Creates and updates Coder Registry workspace templates with agent setup, infrastructure provisioning, and module consumption +--- + +# Coder Templates + +Coder workspace templates are complete workspace definitions that live under `registry//templates//` and provision the infrastructure that workspaces run on. + +## Before You Start + +Before writing or modifying any code: + +1. **Understand the request.** What platform is the template targeting (Docker, AWS, GCP, Azure, Kubernetes)? What kind of workspace (VM, container, devcontainer)? +2. **Research existing templates and modules.** Search the registry for similar templates. Read their `main.tf` to understand patterns for that platform, especially how they handle agent setup, persistent storage, and module consumption. Also search for platform-specific helper modules (e.g. region selectors) that provide ready-made `coder_parameter` blocks; prefer these over hard-coding option lists. +3. **Check provider docs.** Verify the infrastructure provider resources you plan to use. Check both the Coder provider and the platform provider (AWS, Docker, etc.) version-specific docs if needed. +4. **Clarify before building.** If the request is ambiguous (e.g. unclear platform, whether to use devcontainers vs plain VMs, what parameters to expose, or which namespace to use), ask for clarification rather than guessing. Never assume a namespace; always confirm with the user. +5. **Plan the structure.** Decide on infrastructure resources, what `coder_parameter` options to expose, which registry modules to consume, and whether additional files like cloud-init configs are needed. When the user describes requirements in terms of their development needs rather than specific Terraform changes (e.g. "I need Node 20 + Postgres 16" or "make this template work for data science"), summarize what you plan to add or change before proceeding. Keep it brief: list the parameters, modules, and infrastructure changes. Skip this for straightforward requests where the action is clear (e.g. "add the code-server module" or "change the default region to us-west-2"). + +When updating an existing template, read and understand all of its current resources, parameters, and module consumption before making changes. If you observe patterns that deviate from the coder template standards (e.g. missing metadata blocks, hardcoded values that should be parameters, inline implementations that existing modules could replace, missing error handling in scripts), note these to the user as improvement opportunities in your response. + +Features marked as "Premium" in this skill require a Coder Premium license. When your implementation uses a Premium feature, note this in your response to the user so they can verify their deployment supports it. + +## Documentation References + +### Coder + +- Platform docs (latest): +- Version-specific docs: `https://coder.com/docs/@v{MAJOR}.{MINOR}.{PATCH}` (e.g. ) +- Creating templates: +- Extending templates: +- Template parameters: +- Workspace presets: +- Prebuilt workspaces: +- Tasks: +- Agent Boundaries: +- Coder Registry: + +### Coder Terraform provider + +- Provider docs (latest): +- Version-specific provider docs: replace `latest` with a version number (e.g. ) + +Resources: + +| Resource | Docs | +| ---------------- | ------------------------------------------------------------------------------------ | +| `coder_agent` | | +| `coder_app` | | +| `coder_script` | | +| `coder_env` | | +| `coder_metadata` | | +| `coder_ai_task` | | + +Data sources: + +| Data Source | Docs | +| ------------------------ | ----------------------------------------------------------------------------------------------- | +| `coder_parameter` | | +| `coder_workspace` | | +| `coder_workspace_owner` | | +| `coder_provisioner` | | +| `coder_workspace_preset` | | +| `coder_task` | | + +### Terraform providers commonly used in templates + +All provider docs follow `https://registry.terraform.io/providers/ORG/NAME/latest/docs`: + +| Provider | Source | +| ---------- | ---------------------- | +| Docker | `kreuzwerker/docker` | +| AWS | `hashicorp/aws` | +| Azure | `hashicorp/azurerm` | +| GCP | `hashicorp/google` | +| Kubernetes | `hashicorp/kubernetes` | + +Browse all providers: + +## Scaffolding a New Template + +Only use this when creating a brand new template that does not yet exist. When updating an existing template, edit its files directly. + +From repo root: + +```bash +./scripts/new_template.sh namespace/template-name +``` + +Creates `registry//templates//` with: + +- `main.tf`: full workspace Terraform config +- `README.md`: frontmatter and documentation + +If the namespace is new, the script also creates `registry//` with a README. New namespaces additionally need: + +- `registry//.images/avatar.png` (or `.svg`): square image, 400x400px minimum +- The namespace README `avatar` field pointing to `./.images/avatar.png` + +The generated namespace README contains placeholder fields (name, links, avatar) that the user must fill out. After completing the template, inform the user that the namespace README needs to be updated with their information. + +## main.tf + +Templates define the full workspace stack: providers, agent, infrastructure resources, and module consumption. + +```tf +terraform { + required_providers { + coder = { + source = "coder/coder" + } + docker = { + source = "kreuzwerker/docker" + } + } +} + +data "coder_provisioner" "me" {} +data "coder_workspace" "me" {} +data "coder_workspace_owner" "me" {} + +resource "coder_agent" "main" { + arch = data.coder_provisioner.me.arch + os = "linux" + startup_script = <<-EOT + set -e + if [ ! -f ~/.init_done ]; then + cp -rT /etc/skel ~ + touch ~/.init_done + fi + EOT + + 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}" + } + + metadata { + display_name = "CPU Usage" + key = "cpu" + script = "coder stat cpu" + interval = 10 + timeout = 1 + } + + metadata { + display_name = "RAM Usage" + key = "memory" + script = "coder stat mem" + interval = 10 + timeout = 1 + } + + metadata { + display_name = "Home Disk" + key = "disk" + script = "coder stat disk --path $${HOME}" + interval = 60 + timeout = 1 + } + +} + +module "code-server" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/code-server/coder" + version = "~> 1.0" + agent_id = coder_agent.main.id +} + +resource "docker_volume" "home_volume" { + name = "coder-${data.coder_workspace.me.id}-home" + lifecycle { + ignore_changes = all + } + labels { + label = "coder.owner" + value = data.coder_workspace_owner.me.name + } + labels { + label = "coder.workspace_id" + value = data.coder_workspace.me.id + } +} + +resource "docker_container" "workspace" { + count = data.coder_workspace.me.start_count + image = "codercom/enterprise-base:ubuntu" + name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}" + hostname = data.coder_workspace.me.name + entrypoint = ["sh", "-c", replace(coder_agent.main.init_script, "/localhost|127\\.0\\.0\\.1/", "host.docker.internal")] + env = ["CODER_AGENT_TOKEN=${coder_agent.main.token}"] + host { + host = "host.docker.internal" + ip = "host-gateway" + } + volumes { + container_path = "/home/coder" + volume_name = docker_volume.home_volume.name + read_only = false + } + labels { + label = "coder.owner" + value = data.coder_workspace_owner.me.name + } + labels { + label = "coder.workspace_id" + value = data.coder_workspace.me.id + } +} +``` + +Key patterns: + +- Provider version constraints must reflect actual functionality requirements. Only set a minimum `coder` provider version when the template uses a resource, attribute, or behavior introduced in that version. The same applies to infrastructure providers (Docker, AWS, etc.); check provider changelogs to confirm. +- Always include `data.coder_provisioner.me`, `data.coder_workspace.me`, `data.coder_workspace_owner.me` +- Use `data "coder_parameter"` for UI-facing options. When creating a new template, include parameters for the standard configurable options for that platform (e.g. region, CPU, memory, disk size for cloud/VM templates). Use existing templates for the same platform if they exist as a reference for which parameters to include and what defaults to set. +- Use `locals {}` for computed values: username, environment variables, startup scripts, URL assembly +- Use `data.coder_workspace.me.start_count` as `count` on ephemeral resources +- Connect containers/VMs to the agent via `coder_agent.main.init_script` and `CODER_AGENT_TOKEN` +- Add `metadata` blocks for workspace dashboard stats (`coder stat cpu`, `coder stat mem`, etc.) +- Optionally use `display_apps` block to hide specific built-in apps (defaults show all) +- Always search the registry at before implementing any functionality from scratch. If a module already exists for what you need (region selectors, IDE integrations, developer tools, etc.), consume it rather than reimplementing it. When multiple modules serve similar purposes, prefer the actively maintained one and check that you are not using a deprecated or superseded module. +- Do not add comments that narrate what the code does or label sections. Only comment when explaining something non-obvious (e.g. why a workaround exists, a subtle constraint, or an unusual design choice). +- Label infrastructure resources with `coder.owner` and `coder.workspace_id` for tracking orphans +- Use `lifecycle { ignore_changes = all }` on persistent volumes to prevent data loss + +### Additional files + +Templates can include files beyond `main.tf` + `README.md`: + +- `cloud-init/*.tftpl`: cloud-init configs for VM provisioning (AWS, Azure, GCP), loaded via `templatefile()` +- `build/Dockerfile`: custom container images built by the template +- `.tftpl` files: any Terraform template files for scripts, configs, or cloud-init data + +### Presets + +Workspace presets bundle commonly-used parameter combinations into selectable options. When a user creates a workspace, they can pick a preset to auto-fill multiple parameters at once. Define presets with `data "coder_workspace_preset"`: + +```tf +data "coder_workspace_preset" "default" { + name = "Standard Dev Environment" + default = true + + parameters = { + "region" = "us-east-1" + "cpu" = "4" + "memory" = "8" + "container_image" = "codercom/enterprise-base:ubuntu" + } +} +``` + +- The keys in `parameters` must match the `name` attribute of `coder_parameter` data sources in the same template. +- Set `default = true` on at most one preset to pre-select it in the UI. +- A template can define multiple presets for different use cases. +- Optional fields: `description` (context text in UI) and `icon` (e.g. `/emojis/1f680.png`). + +### Prebuilds (Premium) + +Prebuilds maintain an automatically-managed pool of pre-provisioned workspaces for a preset, reducing workspace creation time. This is a Premium feature. Prebuilds are configured as a nested block inside a preset: + +```tf +data "coder_workspace_preset" "goland" { + name = "GoLand: Large" + parameters = { + "jetbrains_ide" = "GO" + "cpu" = "8" + "memory" = "16" + } + + prebuilds { + instances = 3 + + expiration_policy { + ttl = 86400 + } + + scheduling { + timezone = "UTC" + schedule { + cron = "* 8-18 * * 1-5" + instances = 5 + } + } + } +} +``` + +- `instances`: number of prebuilt workspaces to keep in the pool (base count when no schedule matches). +- `expiration_policy.ttl`: seconds before unclaimed prebuilds are cleaned up. +- `scheduling`: scale the pool up or down on a time-based cron schedule. The `cron` minute field must always be `*`. +- The preset must define all required parameters needed to build the workspace. +- When a prebuild is claimed, ownership transfers to the real user. Use `lifecycle { ignore_changes = [...] }` on resources that reference owner-specific values to prevent unnecessary recreation. + +### Task-Oriented Templates + +A template becomes task-capable by adding a `coder_ai_task` resource, which enables the Coder Tasks UI for AI agent workflows. Task templates require three additions on top of a regular template: + +```tf +resource "coder_ai_task" "task" { + count = data.coder_workspace.me.start_count + app_id = module.claude-code[count.index].task_app_id +} + +data "coder_task" "me" {} + +module "claude-code" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/claude-code/coder" + version = "~> 4.0" + agent_id = coder_agent.main.id + workdir = "/home/coder/projects" + ai_prompt = data.coder_task.me.prompt + system_prompt = data.coder_parameter.system_prompt.value + model = "sonnet" + permission_mode = "plan" + enable_boundary = true +} +``` + +- `coder_ai_task`: declares the template as task-capable. Its `app_id` must point to the agent module's `task_app_id` output. +- `data "coder_task"`: reads the user's task prompt. Pass it to the agent module via `ai_prompt`. +- Agent module: consume an AI agent module (`claude-code`, `codex`, etc.) with task-specific variables. Key variables include `ai_prompt`, `system_prompt`, `permission_mode`, and `enable_boundary`. +- Boundaries: set `enable_boundary = true` on the agent module to enable network-level filtering for the AI agent. See for allowlist configuration. +- A `coder_app` with `slug = "preview"` gets special treatment in the Tasks UI navbar. +- Task templates heavily use presets to define scenarios (different repos, system prompts, setup scripts, container images). +- See `registry/coder-labs/templates/tasks-docker` as a reference implementation. + +Docs: + +## README.md + +Required YAML frontmatter: + +```yaml +--- +display_name: Docker Containers +description: Provision Docker containers with persistent home volumes as Coder workspaces +icon: ../../../../.icons/docker.svg +verified: false +tags: [docker, container] +--- +``` + +Content rules: + +- Single H1 heading matching `display_name`, directly below frontmatter +- When increasing header levels, increment by one each time (h1 -> h2 -> h3, not h1 -> h3) +- Opening paragraph describing what the template provisions. Be specific about the platform, compute type, and key capabilities (e.g. "Provision Kubernetes pods on an existing Amazon EKS cluster as Coder workspaces with persistent home volumes") rather than generic (e.g. "AWS Kubernetes template"). The frontmatter `description` field should follow the same principle. +- **Prerequisites** section (infrastructure requirements, provider credentials) +- **Architecture** section (what resources are created, what's ephemeral vs persistent) +- **Customization** section (how to modify startup scripts, add software, configure providers) +- Code fences labeled `tf` (NOT `hcl`) +- Relative icon paths (e.g. `../../../../.icons/`) +- **Do NOT include tables or lists that enumerate variables, parameters, or outputs.** The registry generates variable and output documentation automatically from the Terraform source. Workspace parameter options are visible in the Coder UI. Describe what the template does and how to use it in prose, not by listing every configurable field. +- Use [GFM alerts](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts) for callouts: `> [!NOTE]`, `> [!TIP]`, `> [!IMPORTANT]`, `> [!WARNING]`, `> [!CAUTION]` + +## Icons + +Templates reference icons in the README frontmatter `icon:` field using a relative path to the repo's `.icons/` directory (e.g. `../../../../.icons/aws.svg`). This icon is displayed on the registry website. + +Workflow: + +1. **Check what exists.** List the `.icons/` directory at the repo root for available SVGs. +2. **Use existing icons when they fit.** Most templates use a platform icon (aws, gcp, azure, docker, kubernetes) that already exists. +3. **When an icon doesn't exist,** reference the expected path anyway so the structure is correct. Try to source the official SVG from the platform's branding page or repository. If you can obtain it, add it to `.icons/` in this repo. +4. **Don't substitute a generic icon.** If the platform has its own brand identity, use the correct name even if the file doesn't exist yet. +5. **Notify the user.** After completing the template, inform the user in your response if any icons were referenced but not found. Note that missing icons need to be sourced and added to both this repo's `.icons/` directory and the `coder/coder` repo at `site/static/icon/`. + +## Testing + +Templates do NOT require `.tftest.hcl` or `main.test.ts`. Testing is done by pushing the template to a Coder deployment with `coder templates push`. + +## Commands + +| Task | Command | Scope | +| ---------- | --------------------------------- | ----- | +| Format all | `bun run fmt` | Repo | +| Validate | `./scripts/terraform_validate.sh` | Repo | + +Always run `bun run fmt` before committing. + +## PR Checklist + +- [ ] Template provisions and starts successfully +- [ ] README documents prerequisites and architecture +- [ ] Shell scripts handle errors gracefully (`|| echo "Warning..."` for non-fatal) +- [ ] No hardcoded values that should be configurable +- [ ] No absolute URLs (use relative paths) +- [ ] AI attribution in PR footer (e.g. "Generated with Claude") From 1bbccbad2962af53dcbd42d9f01e3991c307611d Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Wed, 25 Mar 2026 15:26:25 -0500 Subject: [PATCH 02/11] chore: refactore final checks section from pr checklist which is ambiguous for this usage --- .claude/skills/coder-modules/SKILL.md | 21 ++++++++++----------- .claude/skills/coder-templates/SKILL.md | 15 +++++++-------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/.claude/skills/coder-modules/SKILL.md b/.claude/skills/coder-modules/SKILL.md index 705c2a001..72d0501b3 100644 --- a/.claude/skills/coder-modules/SKILL.md +++ b/.claude/skills/coder-modules/SKILL.md @@ -374,8 +374,6 @@ Cleanup of `*.tfstate` files and `modules-test` Docker containers is handled aut | Validate | `./scripts/terraform_validate.sh` | Repo | | Version bump | `.github/scripts/version-bump.sh patch\|minor\|major` | Module dir | -Always run `bun run fmt` before committing. - ## Version Management Bump version via `.github/scripts/version-bump.sh` when modifying modules: @@ -386,13 +384,14 @@ Bump version via `.github/scripts/version-bump.sh` when modifying modules: The script automatically updates `version` references in README usage examples. -## PR Checklist +## Final Checks + +Before considering the work complete, verify: -- [ ] Version bumped if module changed (script also updates README examples) -- [ ] Breaking changes documented (removed inputs, changed defaults, new required variables) -- [ ] New variables have sensible defaults -- [ ] Tests pass: `bun run tftest` and `bun run tstest` -- [ ] Shell scripts handle errors gracefully (`|| echo "Warning..."` for non-fatal) -- [ ] No hardcoded values that should be configurable -- [ ] No absolute URLs (use relative paths) -- [ ] AI attribution in PR footer (e.g. "Generated with Claude") +- New variables have sensible defaults for backward compatibility +- Breaking changes are documented if any inputs were removed, defaults changed, or new required variables added +- Tests pass: `bun run tftest` and `bun run tstest` +- Shell scripts handle errors gracefully (`|| echo "Warning..."` for non-fatal failures) +- No hardcoded values that should be configurable via variables +- No absolute URLs (use relative paths) +- `bun run fmt` has been run diff --git a/.claude/skills/coder-templates/SKILL.md b/.claude/skills/coder-templates/SKILL.md index 4c7ced8ee..9e82b87df 100644 --- a/.claude/skills/coder-templates/SKILL.md +++ b/.claude/skills/coder-templates/SKILL.md @@ -381,13 +381,12 @@ Templates do NOT require `.tftest.hcl` or `main.test.ts`. Testing is done by pus | Format all | `bun run fmt` | Repo | | Validate | `./scripts/terraform_validate.sh` | Repo | -Always run `bun run fmt` before committing. +## Final Checks -## PR Checklist +Before considering the work complete, verify: -- [ ] Template provisions and starts successfully -- [ ] README documents prerequisites and architecture -- [ ] Shell scripts handle errors gracefully (`|| echo "Warning..."` for non-fatal) -- [ ] No hardcoded values that should be configurable -- [ ] No absolute URLs (use relative paths) -- [ ] AI attribution in PR footer (e.g. "Generated with Claude") +- README documents prerequisites and architecture +- Shell scripts handle errors gracefully (`|| echo "Warning..."` for non-fatal failures) +- No hardcoded values that should be configurable via variables or parameters +- No absolute URLs (use relative paths) +- `bun run fmt` has been run From d0b5113c661708d9901b28a9f0a4aaf9148ff6d3 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Wed, 25 Mar 2026 16:06:25 -0500 Subject: [PATCH 03/11] chore: add instruction around verifying module consumption accuracy --- .claude/skills/coder-modules/SKILL.md | 2 +- .claude/skills/coder-templates/SKILL.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.claude/skills/coder-modules/SKILL.md b/.claude/skills/coder-modules/SKILL.md index 72d0501b3..dd7e25870 100644 --- a/.claude/skills/coder-modules/SKILL.md +++ b/.claude/skills/coder-modules/SKILL.md @@ -127,7 +127,7 @@ Key patterns: - Common variable: `agent_id` (string, required, no default) - Common variable: `order` (number, default `null`, controls UI position) - Use `locals {}` for computed values: URL normalization, base64 encoding, `file()` script content, config assembly -- Modules can consume other registry modules via `module` blocks (e.g. `cursor` uses `vscode-desktop-core`, CLI wrappers use `agentapi`) +- Modules can consume other registry modules via `module` blocks (e.g. `cursor` uses `vscode-desktop-core`, CLI wrappers use `agentapi`). Before consuming a module, verify its accepted variables by reading its `main.tf` directly if inside the registry repo, or by reading its page at `https://registry.coder.com/modules//`. Never pass arguments without confirming they exist. - `coder_script` icons use the `/icon/.svg` format. The `display_name` is typically the product name (e.g. "code-server", "Git Clone", "File Browser"). - Do not add comments that narrate what the code does or label sections. Only comment when explaining something non-obvious (e.g. why a workaround exists, a subtle constraint, or an unusual design choice). diff --git a/.claude/skills/coder-templates/SKILL.md b/.claude/skills/coder-templates/SKILL.md index 9e82b87df..4f86ada73 100644 --- a/.claude/skills/coder-templates/SKILL.md +++ b/.claude/skills/coder-templates/SKILL.md @@ -223,6 +223,7 @@ Key patterns: - Add `metadata` blocks for workspace dashboard stats (`coder stat cpu`, `coder stat mem`, etc.) - Optionally use `display_apps` block to hide specific built-in apps (defaults show all) - Always search the registry at before implementing any functionality from scratch. If a module already exists for what you need (region selectors, IDE integrations, developer tools, etc.), consume it rather than reimplementing it. When multiple modules serve similar purposes, prefer the actively maintained one and check that you are not using a deprecated or superseded module. +- Before consuming a module, verify its accepted variables. If you are inside the registry repo, read the module's `main.tf` directly (e.g. `registry/coder/modules/code-server/main.tf`). Otherwise, read the module's page at `https://registry.coder.com/modules//` which includes the full source and variable definitions. Never pass arguments to a module without confirming they exist. - Do not add comments that narrate what the code does or label sections. Only comment when explaining something non-obvious (e.g. why a workaround exists, a subtle constraint, or an unusual design choice). - Label infrastructure resources with `coder.owner` and `coder.workspace_id` for tracking orphans - Use `lifecycle { ignore_changes = all }` on persistent volumes to prevent data loss From 5d16a73d8079541daf4d7a02dd54dbbb926d7170 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Wed, 25 Mar 2026 16:15:34 -0500 Subject: [PATCH 04/11] chore: update verification steps in SKILL.md to ensure formatting and validation checks are clear --- .claude/skills/coder-templates/SKILL.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.claude/skills/coder-templates/SKILL.md b/.claude/skills/coder-templates/SKILL.md index 4f86ada73..802691460 100644 --- a/.claude/skills/coder-templates/SKILL.md +++ b/.claude/skills/coder-templates/SKILL.md @@ -386,8 +386,9 @@ Templates do NOT require `.tftest.hcl` or `main.test.ts`. Testing is done by pus Before considering the work complete, verify: +- `terraform init && terraform validate` passes in the template directory +- `bun run fmt` has been run - README documents prerequisites and architecture - Shell scripts handle errors gracefully (`|| echo "Warning..."` for non-fatal failures) - No hardcoded values that should be configurable via variables or parameters - No absolute URLs (use relative paths) -- `bun run fmt` has been run From 6b72db47f5dc82809b8fbc1b85d51151ead739e8 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Wed, 25 Mar 2026 16:34:17 -0500 Subject: [PATCH 05/11] chore: enhance module consumption guidelines in skills to catch more nuanced module requirements when agent consumes modules. --- .claude/skills/coder-modules/SKILL.md | 2 +- .claude/skills/coder-templates/SKILL.md | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.claude/skills/coder-modules/SKILL.md b/.claude/skills/coder-modules/SKILL.md index dd7e25870..7dc01749f 100644 --- a/.claude/skills/coder-modules/SKILL.md +++ b/.claude/skills/coder-modules/SKILL.md @@ -127,7 +127,7 @@ Key patterns: - Common variable: `agent_id` (string, required, no default) - Common variable: `order` (number, default `null`, controls UI position) - Use `locals {}` for computed values: URL normalization, base64 encoding, `file()` script content, config assembly -- Modules can consume other registry modules via `module` blocks (e.g. `cursor` uses `vscode-desktop-core`, CLI wrappers use `agentapi`). Before consuming a module, verify its accepted variables by reading its `main.tf` directly if inside the registry repo, or by reading its page at `https://registry.coder.com/modules//`. Never pass arguments without confirming they exist. +- Modules can consume other registry modules via `module` blocks (e.g. `cursor` uses `vscode-desktop-core`, CLI wrappers use `agentapi`). Before consuming a module, read its `main.tf` and `README.md` to understand the full interface: accepted variables, outputs, prerequisites, and runtime requirements. If you are inside the registry repo, read these files directly. Otherwise, read the module's page at `https://registry.coder.com/modules//` which includes the full source, README, and variable definitions. Never pass arguments without confirming they exist. - `coder_script` icons use the `/icon/.svg` format. The `display_name` is typically the product name (e.g. "code-server", "Git Clone", "File Browser"). - Do not add comments that narrate what the code does or label sections. Only comment when explaining something non-obvious (e.g. why a workaround exists, a subtle constraint, or an unusual design choice). diff --git a/.claude/skills/coder-templates/SKILL.md b/.claude/skills/coder-templates/SKILL.md index 802691460..4eede2f45 100644 --- a/.claude/skills/coder-templates/SKILL.md +++ b/.claude/skills/coder-templates/SKILL.md @@ -223,7 +223,8 @@ Key patterns: - Add `metadata` blocks for workspace dashboard stats (`coder stat cpu`, `coder stat mem`, etc.) - Optionally use `display_apps` block to hide specific built-in apps (defaults show all) - Always search the registry at before implementing any functionality from scratch. If a module already exists for what you need (region selectors, IDE integrations, developer tools, etc.), consume it rather than reimplementing it. When multiple modules serve similar purposes, prefer the actively maintained one and check that you are not using a deprecated or superseded module. -- Before consuming a module, verify its accepted variables. If you are inside the registry repo, read the module's `main.tf` directly (e.g. `registry/coder/modules/code-server/main.tf`). Otherwise, read the module's page at `https://registry.coder.com/modules//` which includes the full source and variable definitions. Never pass arguments to a module without confirming they exist. +- Before consuming a module, read its `main.tf` and `README.md` to understand the full interface: accepted variables, outputs, prerequisites, and runtime requirements. If you are inside the registry repo, read these files directly. Otherwise, read the module's page at `https://registry.coder.com/modules//`. Never pass arguments without confirming they exist. +- After identifying a module's prerequisites, verify the template's base image satisfies them. If it lacks a required tool, either switch to an image that includes it or ensure the prerequisite is installed before the module's script runs. These runtime issues are not caught by `terraform validate`; they only surface when the workspace starts. - Do not add comments that narrate what the code does or label sections. Only comment when explaining something non-obvious (e.g. why a workaround exists, a subtle constraint, or an unusual design choice). - Label infrastructure resources with `coder.owner` and `coder.workspace_id` for tracking orphans - Use `lifecycle { ignore_changes = all }` on persistent volumes to prevent data loss From d48585fef4adaff95dd0041452edf1859e7730d4 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Wed, 25 Mar 2026 16:44:37 -0500 Subject: [PATCH 06/11] chore: clarify asset path requirements in SKILL.md for coder-modules and coder-templates, and clarify coder data source usage nuance --- .claude/skills/coder-modules/SKILL.md | 4 ++-- .claude/skills/coder-templates/SKILL.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.claude/skills/coder-modules/SKILL.md b/.claude/skills/coder-modules/SKILL.md index 7dc01749f..43a87b88d 100644 --- a/.claude/skills/coder-modules/SKILL.md +++ b/.claude/skills/coder-modules/SKILL.md @@ -391,7 +391,7 @@ Before considering the work complete, verify: - New variables have sensible defaults for backward compatibility - Breaking changes are documented if any inputs were removed, defaults changed, or new required variables added - Tests pass: `bun run tftest` and `bun run tstest` +- `bun run fmt` has been run - Shell scripts handle errors gracefully (`|| echo "Warning..."` for non-fatal failures) - No hardcoded values that should be configurable via variables -- No absolute URLs (use relative paths) -- `bun run fmt` has been run +- Asset and icon paths in frontmatter and Terraform must be relative (e.g. `../../../../.icons/`), not absolute. External hyperlinks to docs or other websites are fine. diff --git a/.claude/skills/coder-templates/SKILL.md b/.claude/skills/coder-templates/SKILL.md index 4eede2f45..30c729647 100644 --- a/.claude/skills/coder-templates/SKILL.md +++ b/.claude/skills/coder-templates/SKILL.md @@ -215,7 +215,7 @@ resource "docker_container" "workspace" { Key patterns: - Provider version constraints must reflect actual functionality requirements. Only set a minimum `coder` provider version when the template uses a resource, attribute, or behavior introduced in that version. The same applies to infrastructure providers (Docker, AWS, etc.); check provider changelogs to confirm. -- Always include `data.coder_provisioner.me`, `data.coder_workspace.me`, `data.coder_workspace_owner.me` +- Include `data.coder_workspace.me` and `data.coder_workspace_owner.me` for workspace and owner metadata. Include `data.coder_provisioner.me` only when you need the provisioner's `arch` or `os` for `coder_agent` (typical for Docker, Kubernetes, Incus); omit when the workspace OS/arch is fixed (e.g. cloud VMs with a known image). - Use `data "coder_parameter"` for UI-facing options. When creating a new template, include parameters for the standard configurable options for that platform (e.g. region, CPU, memory, disk size for cloud/VM templates). Use existing templates for the same platform if they exist as a reference for which parameters to include and what defaults to set. - Use `locals {}` for computed values: username, environment variables, startup scripts, URL assembly - Use `data.coder_workspace.me.start_count` as `count` on ephemeral resources @@ -392,4 +392,4 @@ Before considering the work complete, verify: - README documents prerequisites and architecture - Shell scripts handle errors gracefully (`|| echo "Warning..."` for non-fatal failures) - No hardcoded values that should be configurable via variables or parameters -- No absolute URLs (use relative paths) +- Asset and icon paths in frontmatter and Terraform must be relative (e.g. `../../../../.icons/`), not absolute. External hyperlinks to docs or other websites are fine. From 67a74ffca1969f2f78a5dabf8816f369092a3d1d Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Wed, 25 Mar 2026 17:28:51 -0500 Subject: [PATCH 07/11] chore: refine parameter usage guidance in SKILL.md for coder-templates, emphasizing user-facing options and sensible defaults --- .claude/skills/coder-templates/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.claude/skills/coder-templates/SKILL.md b/.claude/skills/coder-templates/SKILL.md index 30c729647..349a87046 100644 --- a/.claude/skills/coder-templates/SKILL.md +++ b/.claude/skills/coder-templates/SKILL.md @@ -216,7 +216,7 @@ Key patterns: - Provider version constraints must reflect actual functionality requirements. Only set a minimum `coder` provider version when the template uses a resource, attribute, or behavior introduced in that version. The same applies to infrastructure providers (Docker, AWS, etc.); check provider changelogs to confirm. - Include `data.coder_workspace.me` and `data.coder_workspace_owner.me` for workspace and owner metadata. Include `data.coder_provisioner.me` only when you need the provisioner's `arch` or `os` for `coder_agent` (typical for Docker, Kubernetes, Incus); omit when the workspace OS/arch is fixed (e.g. cloud VMs with a known image). -- Use `data "coder_parameter"` for UI-facing options. When creating a new template, include parameters for the standard configurable options for that platform (e.g. region, CPU, memory, disk size for cloud/VM templates). Use existing templates for the same platform if they exist as a reference for which parameters to include and what defaults to set. +- Use `data "coder_parameter"` for user-facing knobs. For new templates, include standard options for the platform (e.g. region, CPU, memory, disk size for cloud/VM templates); align with same-platform templates. Expose stated preferences as the parameter `default` with additional sensible `option` values unless the user explicitly restricts that dimension. - Use `locals {}` for computed values: username, environment variables, startup scripts, URL assembly - Use `data.coder_workspace.me.start_count` as `count` on ephemeral resources - Connect containers/VMs to the agent via `coder_agent.main.init_script` and `CODER_AGENT_TOKEN` From 8257aeb1755f8b9e9706c74c01a911253cf27dd7 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Thu, 26 Mar 2026 12:22:33 -0500 Subject: [PATCH 08/11] chore: add more instruction from david code review --- .claude/skills/coder-modules/SKILL.md | 44 +++++++++++++++---------- .claude/skills/coder-templates/SKILL.md | 38 ++++++++++++--------- 2 files changed, 49 insertions(+), 33 deletions(-) diff --git a/.claude/skills/coder-modules/SKILL.md b/.claude/skills/coder-modules/SKILL.md index 43a87b88d..c3a5b5fd6 100644 --- a/.claude/skills/coder-modules/SKILL.md +++ b/.claude/skills/coder-modules/SKILL.md @@ -57,6 +57,8 @@ From repo root: ./scripts/new_module.sh namespace/module-name ``` +Names must be lowercase alphanumeric with hyphens or underscores (e.g. `coder/my-tool`). + Creates `registry//modules//` with: - `main.tf`: Terraform config with coder provider @@ -66,10 +68,10 @@ Creates `registry//modules//` with: If the namespace is new, the script also creates `registry//` with a README. New namespaces additionally need: -- `registry//.images/avatar.png` (or `.svg`): square image, 400x400px minimum -- The namespace README `avatar` field pointing to `./.images/avatar.png` +- `registry//.images/avatar.svg` (or `.png`): square image, 400x400px minimum +- The namespace README `avatar` field pointing to `./.images/avatar.svg` -The generated namespace README contains placeholder fields (name, links, avatar) that the user must fill out. After completing the module, inform the user that the namespace README needs to be updated with their information. +The generated namespace README contains placeholder fields (`display_name`, `bio`, `status`, `github`, `avatar`, etc.) that the user must fill out. The `status` field is required and must be `official`, `partner`, or `community` (typically `community` for new contributors). After completing the module, inform the user that the namespace README needs to be updated with their information. ## main.tf @@ -202,9 +204,11 @@ resource "coder_script" "my_tool" { Use `$${VAR}` (double dollar) in the shell script for Terraform `templatefile` escaping. +If a script sources external files (`$HOME/.bashrc`, `/etc/bashrc`, `/etc/os-release`), the `source` statement must come before `set -u`; CI enforces this ordering. + ### `scripts/` directory + `file()` (complex modules) -Separate `scripts/install.sh` and `scripts/start.sh` loaded via `file()` into `locals`, then passed to a child module or encoded inline. Used by `claude-code`, `copilot`, `codex`, `cursor-cli`, `amazon-q`. +Separate `scripts/install.sh` and `scripts/start.sh` loaded via `file()` into `locals`, then passed to a child module or encoded inline. Used by `coder/claude-code`, `coder-labs/copilot`, `coder-labs/codex`, `coder-labs/cursor-cli`, `coder/amazon-q` for example. ```tf locals { @@ -213,7 +217,7 @@ locals { } ``` -Use `file()` when scripts don't need Terraform variable interpolation. For config templates, use a `templates/` directory with `templatefile()` (e.g. `amazon-q/templates/agent-config.json.tpl`). +Use `file()` when scripts don't need Terraform variable interpolation. For config templates, use a `templates/` directory with `templatefile()` (e.g. `coder/amazon-q/templates/agent-config.json.tpl`). ### Inline heredoc (minimal modules) @@ -339,7 +343,7 @@ describe("my-tool", () => { **Terraform helpers:** - `runTerraformInit(dir)`: runs `terraform init`. -- `runTerraformApply(dir, vars, customEnv?)`: runs `terraform apply` with a random state file and returns `TerraformState`. Variables are passed as `TF_VAR_*`. Safe to run in parallel. +- `runTerraformApply(dir, vars, customEnv?)`: runs `terraform apply` with a random state file and returns `TerraformState`. Variables are passed as `TF_VAR_*`. Safe to run in parallel. `TerraformState` has `outputs: Record` and `resources: TerraformStateResource[]`. - `testRequiredVariables(dir, vars)`: auto-generates test cases (one success with all vars, plus one per var verifying apply fails without it). Pass `{}` if there are no required vars. - `findResourceInstance(state, type, name?)`: finds the first resource instance by type. Throws if not found. Optionally filters by name. @@ -347,7 +351,7 @@ describe("my-tool", () => { - `runContainer(image, init?)`: starts a detached container and returns its ID. Labeled `modules-test=true` for auto-cleanup. - `removeContainer(id)`: force-removes a container. -- `execContainer(id, cmd, args?)`: runs a command in a container and returns `{ exitCode, stdout, stderr }`. +- `execContainer(id, cmd[], args?[])`: runs a command in a container and returns `{ exitCode, stdout, stderr }`. - `executeScriptInContainer(state, image, shell?, before?)`: finds `coder_script` in state, runs it in a container, and returns `{ exitCode, stdout: string[], stderr: string[] }`. **File helpers:** @@ -364,15 +368,17 @@ Cleanup of `*.tfstate` files and `modules-test` Docker containers is handled aut ## Commands -| Task | Command | Scope | -| ---------------- | ----------------------------------------------------- | ---------- | -| Format all | `bun run fmt` | Repo | -| Terraform tests | `bun run tftest` | Repo | -| TypeScript tests | `bun run tstest` | Repo | -| Single TF test | `terraform init -upgrade && terraform test -verbose` | Module dir | -| Single TS test | `bun test main.test.ts` | Module dir | -| Validate | `./scripts/terraform_validate.sh` | Repo | -| Version bump | `.github/scripts/version-bump.sh patch\|minor\|major` | Module dir | +| Task | Command | Scope | +| ---------------- | ------------------------------------------------------- | ---------- | +| Format all | `bun run fmt` | Repo | +| Terraform tests | `bun run tftest` | Repo | +| TypeScript tests | `bun run tstest` | Repo | +| Single TF test | `terraform init -upgrade && terraform test -verbose` | Module dir | +| Single TS test | `bun test main.test.ts` | Module dir | +| Validate | `./scripts/terraform_validate.sh` | Repo | +| ShellCheck | `bun run shellcheck` | Repo | +| README validate | `go build ./cmd/readmevalidation && ./readmevalidation` | Repo | +| Version bump | `.github/scripts/version-bump.sh patch\|minor\|major` | Repo | ## Version Management @@ -388,10 +394,12 @@ The script automatically updates `version` references in README usage examples. Before considering the work complete, verify: -- New variables have sensible defaults for backward compatibility -- Breaking changes are documented if any inputs were removed, defaults changed, or new required variables added - Tests pass: `bun run tftest` and `bun run tstest` - `bun run fmt` has been run +- `bun run shellcheck` passes if the module includes shell scripts +- `go build ./cmd/readmevalidation && ./readmevalidation` passes (validates frontmatter, icon paths, body structure, GFM alerts) +- New variables have sensible defaults for backward compatibility +- Breaking changes are documented if any inputs were removed, defaults changed, or new required variables added - Shell scripts handle errors gracefully (`|| echo "Warning..."` for non-fatal failures) - No hardcoded values that should be configurable via variables - Asset and icon paths in frontmatter and Terraform must be relative (e.g. `../../../../.icons/`), not absolute. External hyperlinks to docs or other websites are fine. diff --git a/.claude/skills/coder-templates/SKILL.md b/.claude/skills/coder-templates/SKILL.md index 349a87046..247d06479 100644 --- a/.claude/skills/coder-templates/SKILL.md +++ b/.claude/skills/coder-templates/SKILL.md @@ -12,7 +12,7 @@ Coder workspace templates are complete workspace definitions that live under `re Before writing or modifying any code: 1. **Understand the request.** What platform is the template targeting (Docker, AWS, GCP, Azure, Kubernetes)? What kind of workspace (VM, container, devcontainer)? -2. **Research existing templates and modules.** Search the registry for similar templates. Read their `main.tf` to understand patterns for that platform, especially how they handle agent setup, persistent storage, and module consumption. Also search for platform-specific helper modules (e.g. region selectors) that provide ready-made `coder_parameter` blocks; prefer these over hard-coding option lists. +2. **Research existing templates and modules.** Look under `registry/` in this repo for similar templates and modules first; if you are not in the repo or cannot find a match, browse . Read `main.tf` to understand patterns for that platform, especially how they handle agent setup, persistent storage, and module consumption. Prefer platform-specific helper modules (e.g. region selectors) that provide ready-made `coder_parameter` blocks over hard-coding option lists. 3. **Check provider docs.** Verify the infrastructure provider resources you plan to use. Check both the Coder provider and the platform provider (AWS, Docker, etc.) version-specific docs if needed. 4. **Clarify before building.** If the request is ambiguous (e.g. unclear platform, whether to use devcontainers vs plain VMs, what parameters to expose, or which namespace to use), ask for clarification rather than guessing. Never assume a namespace; always confirm with the user. 5. **Plan the structure.** Decide on infrastructure resources, what `coder_parameter` options to expose, which registry modules to consume, and whether additional files like cloud-init configs are needed. When the user describes requirements in terms of their development needs rather than specific Terraform changes (e.g. "I need Node 20 + Postgres 16" or "make this template work for data science"), summarize what you plan to add or change before proceeding. Keep it brief: list the parameters, modules, and infrastructure changes. Skip this for straightforward requests where the action is clear (e.g. "add the code-server module" or "change the default region to us-west-2"). @@ -74,6 +74,7 @@ All provider docs follow `https://registry.terraform.io/providers/ORG/NAME/lates | Azure | `hashicorp/azurerm` | | GCP | `hashicorp/google` | | Kubernetes | `hashicorp/kubernetes` | +| Cloud-Init | `hashicorp/cloudinit` | Browse all providers: @@ -87,6 +88,8 @@ From repo root: ./scripts/new_template.sh namespace/template-name ``` +Names must be lowercase alphanumeric with hyphens or underscores (e.g. `my-org/python-docker`). + Creates `registry//templates//` with: - `main.tf`: full workspace Terraform config @@ -94,10 +97,10 @@ Creates `registry//templates//` with: If the namespace is new, the script also creates `registry//` with a README. New namespaces additionally need: -- `registry//.images/avatar.png` (or `.svg`): square image, 400x400px minimum -- The namespace README `avatar` field pointing to `./.images/avatar.png` +- `registry//.images/avatar.svg` (or `.png`): square image, 400x400px minimum +- The namespace README `avatar` field pointing to `./.images/avatar.svg` -The generated namespace README contains placeholder fields (name, links, avatar) that the user must fill out. After completing the template, inform the user that the namespace README needs to be updated with their information. +The generated namespace README contains placeholder fields (`display_name`, `bio`, `status`, `github`, `avatar`, etc.) that the user must fill out. The `status` field is required and must be `official`, `partner`, or `community` (typically `community` for new contributors). After completing the template, inform the user that the namespace README needs to be updated with their information. ## main.tf @@ -216,24 +219,26 @@ Key patterns: - Provider version constraints must reflect actual functionality requirements. Only set a minimum `coder` provider version when the template uses a resource, attribute, or behavior introduced in that version. The same applies to infrastructure providers (Docker, AWS, etc.); check provider changelogs to confirm. - Include `data.coder_workspace.me` and `data.coder_workspace_owner.me` for workspace and owner metadata. Include `data.coder_provisioner.me` only when you need the provisioner's `arch` or `os` for `coder_agent` (typical for Docker, Kubernetes, Incus); omit when the workspace OS/arch is fixed (e.g. cloud VMs with a known image). -- Use `data "coder_parameter"` for user-facing knobs. For new templates, include standard options for the platform (e.g. region, CPU, memory, disk size for cloud/VM templates); align with same-platform templates. Expose stated preferences as the parameter `default` with additional sensible `option` values unless the user explicitly restricts that dimension. +- Use `data "coder_parameter"` for user-facing knobs. For new templates, include standard options for the platform (e.g. region, CPU, memory, disk size for cloud/VM templates); align with same-platform templates in `registry/` when available, otherwise use the registry website. Expose stated preferences as the parameter `default` with additional sensible `option` values unless the user explicitly restricts that dimension. - Use `locals {}` for computed values: username, environment variables, startup scripts, URL assembly - Use `data.coder_workspace.me.start_count` as `count` on ephemeral resources - Connect containers/VMs to the agent via `coder_agent.main.init_script` and `CODER_AGENT_TOKEN` - Add `metadata` blocks for workspace dashboard stats (`coder stat cpu`, `coder stat mem`, etc.) +- Use `coder_metadata` on the primary compute resource to surface key details (region, instance type, image, disk size) in the workspace dashboard - Optionally use `display_apps` block to hide specific built-in apps (defaults show all) -- Always search the registry at before implementing any functionality from scratch. If a module already exists for what you need (region selectors, IDE integrations, developer tools, etc.), consume it rather than reimplementing it. When multiple modules serve similar purposes, prefer the actively maintained one and check that you are not using a deprecated or superseded module. -- Before consuming a module, read its `main.tf` and `README.md` to understand the full interface: accepted variables, outputs, prerequisites, and runtime requirements. If you are inside the registry repo, read these files directly. Otherwise, read the module's page at `https://registry.coder.com/modules//`. Never pass arguments without confirming they exist. +- Before implementing functionality from scratch, look for an existing module under `registry/*/modules/` in this repo; if you cannot find one or are not in the repo, search . If a module already exists for what you need, consume it rather than reimplementing it. When multiple modules serve similar purposes, prefer the actively maintained one and check that you are not using a deprecated or superseded module. +- Before consuming a module, read its `main.tf` and `README.md` to understand the full interface: accepted variables, outputs, prerequisites, and runtime requirements. Prefer paths under `registry//modules//` in this workspace; otherwise use `https://registry.coder.com/modules//`. Never pass arguments without confirming they exist. - After identifying a module's prerequisites, verify the template's base image satisfies them. If it lacks a required tool, either switch to an image that includes it or ensure the prerequisite is installed before the module's script runs. These runtime issues are not caught by `terraform validate`; they only surface when the workspace starts. -- Do not add comments that narrate what the code does or label sections. Only comment when explaining something non-obvious (e.g. why a workaround exists, a subtle constraint, or an unusual design choice). +- Module source URLs use `registry.coder.com///coder`. Older templates may use `registry.coder.com/modules/...`; prefer the shorter form when writing new modules or templates. - Label infrastructure resources with `coder.owner` and `coder.workspace_id` for tracking orphans - Use `lifecycle { ignore_changes = all }` on persistent volumes to prevent data loss +- Do not add comments that narrate what the code does or label sections. Only comment when explaining something non-obvious (e.g. why a workaround exists, a subtle constraint, or an unusual design choice). ### Additional files Templates can include files beyond `main.tf` + `README.md`: -- `cloud-init/*.tftpl`: cloud-init configs for VM provisioning (AWS, Azure, GCP), loaded via `templatefile()` +- `cloud-init/*.tftpl`: cloud-init configs for VM provisioning (AWS, Azure, GCP), loaded via `templatefile()`. Prefer this subdirectory over placing cloud-init files at the template root. - `build/Dockerfile`: custom container images built by the template - `.tftpl` files: any Terraform template files for scripts, configs, or cloud-init data @@ -354,7 +359,6 @@ Content rules: - Opening paragraph describing what the template provisions. Be specific about the platform, compute type, and key capabilities (e.g. "Provision Kubernetes pods on an existing Amazon EKS cluster as Coder workspaces with persistent home volumes") rather than generic (e.g. "AWS Kubernetes template"). The frontmatter `description` field should follow the same principle. - **Prerequisites** section (infrastructure requirements, provider credentials) - **Architecture** section (what resources are created, what's ephemeral vs persistent) -- **Customization** section (how to modify startup scripts, add software, configure providers) - Code fences labeled `tf` (NOT `hcl`) - Relative icon paths (e.g. `../../../../.icons/`) - **Do NOT include tables or lists that enumerate variables, parameters, or outputs.** The registry generates variable and output documentation automatically from the Terraform source. Workspace parameter options are visible in the Coder UI. Describe what the template does and how to use it in prose, not by listing every configurable field. @@ -378,10 +382,12 @@ Templates do NOT require `.tftest.hcl` or `main.test.ts`. Testing is done by pus ## Commands -| Task | Command | Scope | -| ---------- | --------------------------------- | ----- | -| Format all | `bun run fmt` | Repo | -| Validate | `./scripts/terraform_validate.sh` | Repo | +| Task | Command | Scope | +| --------------- | ------------------------------------------------------- | ----- | +| Format all | `bun run fmt` | Repo | +| Validate | `./scripts/terraform_validate.sh` | Repo | +| ShellCheck | `bun run shellcheck` | Repo | +| README validate | `go build ./cmd/readmevalidation && ./readmevalidation` | Repo | ## Final Checks @@ -389,7 +395,9 @@ Before considering the work complete, verify: - `terraform init && terraform validate` passes in the template directory - `bun run fmt` has been run +- `bun run shellcheck` passes if the template includes shell scripts +- `go build ./cmd/readmevalidation && ./readmevalidation` passes (validates frontmatter, icon paths, body structure, GFM alerts) - README documents prerequisites and architecture -- Shell scripts handle errors gracefully (`|| echo "Warning..."` for non-fatal failures) +- Shell scripts handle errors gracefully (`|| echo "Warning..."` for non-fatal failures). If a script sources external files (`$HOME/.bashrc`, `/etc/bashrc`, `/etc/os-release`), the `source` must come before `set -u`; CI enforces this ordering. - No hardcoded values that should be configurable via variables or parameters - Asset and icon paths in frontmatter and Terraform must be relative (e.g. `../../../../.icons/`), not absolute. External hyperlinks to docs or other websites are fine. From fc292388776c08eaebf7c7b8249528e67f03f83c Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Thu, 26 Mar 2026 15:10:52 -0500 Subject: [PATCH 09/11] chore: enhance guidance on implementation standards in SKILL.md for coder-modules and coder-templates, emphasizing the importance of proper structure and user-facing options --- .claude/skills/coder-modules/SKILL.md | 90 ++++--------- .claude/skills/coder-templates/SKILL.md | 170 ++++++------------------ 2 files changed, 71 insertions(+), 189 deletions(-) diff --git a/.claude/skills/coder-modules/SKILL.md b/.claude/skills/coder-modules/SKILL.md index c3a5b5fd6..94c347d95 100644 --- a/.claude/skills/coder-modules/SKILL.md +++ b/.claude/skills/coder-modules/SKILL.md @@ -17,6 +17,8 @@ Before writing or modifying any code: 4. **Clarify before building.** If the request is ambiguous (e.g. unclear which Coder resource to use, whether a `coder_app` vs `coder_script` is appropriate, what variables to expose, or which namespace to use), ask for clarification rather than guessing. Never assume a namespace; always confirm with the user. 5. **Plan the structure.** Decide on script organization (root `run.sh`, `scripts/` directory, or inline), what variables to expose, and what tests to write. +Always prefer the proper implementation over a simpler shortcut. Modules are infrastructure that users depend on. Doing less work is not the same as reducing complexity if it leaves the module incomplete or fragile. + ## Documentation References ### Coder @@ -61,7 +63,7 @@ Names must be lowercase alphanumeric with hyphens or underscores (e.g. `coder/my Creates `registry//modules//` with: -- `main.tf`: Terraform config with coder provider +- `main.tf`: Terraform config with common resource patterns and variables — read this as the primary reference for module structure - `README.md`: frontmatter and usage examples - `MODULE_NAME.tftest.hcl`: Terraform native tests - `run.sh`: install/start-up script template @@ -71,57 +73,11 @@ If the namespace is new, the script also creates `registry//` with a - `registry//.images/avatar.svg` (or `.png`): square image, 400x400px minimum - The namespace README `avatar` field pointing to `./.images/avatar.svg` -The generated namespace README contains placeholder fields (`display_name`, `bio`, `status`, `github`, `avatar`, etc.) that the user must fill out. The `status` field is required and must be `official`, `partner`, or `community` (typically `community` for new contributors). After completing the module, inform the user that the namespace README needs to be updated with their information. - -## main.tf - -```tf -terraform { - required_version = ">= 1.0" +The scaffolding script does not create the `.images/` directory or avatar file. When a new namespace is created, create `registry//.images/` and add a placeholder `avatar.svg` so the directory structure is ready for the user to replace with their real avatar. - required_providers { - coder = { - source = "coder/coder" - version = ">= 2.5" - } - } -} +The generated namespace README contains placeholder fields (`display_name`, `bio`, `status`, `github`, `avatar`, etc.) that the user must fill out. The `status` field is required and must be `official`, `partner`, or `community` (typically `community` for new contributors). -variable "agent_id" { - type = string - description = "The ID of a Coder agent." -} - -resource "coder_script" "my_tool" { - agent_id = var.agent_id - display_name = "My Tool" - icon = local.icon_url - script = templatefile("${path.module}/run.sh", { - LOG_PATH : var.log_path, - }) - run_on_start = true - run_on_stop = false -} - -resource "coder_app" "my_tool" { - agent_id = var.agent_id - slug = "my-tool" - display_name = "My Tool" - url = "http://localhost:${var.port}" - icon = local.icon_url - subdomain = false - share = "owner" - order = var.order - - healthcheck { - url = "http://localhost:${var.port}/healthz" - interval = 5 - threshold = 6 - } -} -``` - -Key patterns: +## Key Patterns - Provider version constraints must reflect actual functionality requirements. Only raise the minimum `coder` provider version (e.g. `>= 2.5` to `>= 2.8`) when the module uses a resource, attribute, or behavior introduced in that version; check the provider changelog to confirm. - Variable names MUST be `snake_case` (no hyphens; validation rejects them) @@ -130,6 +86,8 @@ Key patterns: - Common variable: `order` (number, default `null`, controls UI position) - Use `locals {}` for computed values: URL normalization, base64 encoding, `file()` script content, config assembly - Modules can consume other registry modules via `module` blocks (e.g. `cursor` uses `vscode-desktop-core`, CLI wrappers use `agentapi`). Before consuming a module, read its `main.tf` and `README.md` to understand the full interface: accepted variables, outputs, prerequisites, and runtime requirements. If you are inside the registry repo, read these files directly. Otherwise, read the module's page at `https://registry.coder.com/modules//` which includes the full source, README, and variable definitions. Never pass arguments without confirming they exist. +- Most modules expose configuration via `variable` blocks, letting the template pass values. Use `coder_parameter` inside a module only when the module needs to present a UI choice directly to the workspace user (e.g. region selectors, IDE pickers). +- For parameter-only modules (region selectors, etc.), use `dynamic "option"` with `for_each` from a `locals` map and expose an `output` for the selected value. - `coder_script` icons use the `/icon/.svg` format. The `display_name` is typically the product name (e.g. "code-server", "Git Clone", "File Browser"). - Do not add comments that narrate what the code does or label sections. Only comment when explaining something non-obvious (e.g. why a workaround exists, a subtle constraint, or an unusual design choice). @@ -180,7 +138,7 @@ Workflow: 2. **Use existing icons when they fit.** If the tool already has an icon in `.icons/` and `/icon/`, use those. 3. **When an icon doesn't exist,** reference the expected path anyway (e.g. `../../../../.icons/my-tool.svg` and `/icon/my-tool.svg`) so the structure is correct. Try to source the official SVG from the tool's branding page or repository. If you can obtain it, add it to `.icons/` in this repo. 4. **Don't substitute a generic icon.** If the tool has its own brand identity, use the correct name even if the file doesn't exist yet. Don't fall back to generic icons like `coder.svg` or `terminal.svg`. -5. **Notify the user.** After completing the module, inform the user in your response if any icons were referenced but not found. Note that missing icons need to be sourced and added to both this repo's `.icons/` directory and the `coder/coder` repo at `site/static/icon/`. +5. **Track missing icons** so you can report them in your response. ## Scripts @@ -368,17 +326,16 @@ Cleanup of `*.tfstate` files and `modules-test` Docker containers is handled aut ## Commands -| Task | Command | Scope | -| ---------------- | ------------------------------------------------------- | ---------- | -| Format all | `bun run fmt` | Repo | -| Terraform tests | `bun run tftest` | Repo | -| TypeScript tests | `bun run tstest` | Repo | -| Single TF test | `terraform init -upgrade && terraform test -verbose` | Module dir | -| Single TS test | `bun test main.test.ts` | Module dir | -| Validate | `./scripts/terraform_validate.sh` | Repo | -| ShellCheck | `bun run shellcheck` | Repo | -| README validate | `go build ./cmd/readmevalidation && ./readmevalidation` | Repo | -| Version bump | `.github/scripts/version-bump.sh patch\|minor\|major` | Repo | +| Task | Command | Scope | +| ---------------- | ----------------------------------------------------- | ---------- | +| Format all | `bun run fmt` | Repo | +| Terraform tests | `bun run tftest` | Repo | +| TypeScript tests | `bun run tstest` | Repo | +| Single TF test | `terraform init -upgrade && terraform test -verbose` | Module dir | +| Single TS test | `bun test main.test.ts` | Module dir | +| Validate | `./scripts/terraform_validate.sh` | Repo | +| ShellCheck | `bun run shellcheck` | Repo | +| Version bump | `.github/scripts/version-bump.sh patch\|minor\|major` | Repo | ## Version Management @@ -397,9 +354,16 @@ Before considering the work complete, verify: - Tests pass: `bun run tftest` and `bun run tstest` - `bun run fmt` has been run - `bun run shellcheck` passes if the module includes shell scripts -- `go build ./cmd/readmevalidation && ./readmevalidation` passes (validates frontmatter, icon paths, body structure, GFM alerts) - New variables have sensible defaults for backward compatibility - Breaking changes are documented if any inputs were removed, defaults changed, or new required variables added - Shell scripts handle errors gracefully (`|| echo "Warning..."` for non-fatal failures) - No hardcoded values that should be configurable via variables - Asset and icon paths in frontmatter and Terraform must be relative (e.g. `../../../../.icons/`), not absolute. External hyperlinks to docs or other websites are fine. + +## Response to the User + +In your response, include: + +- If a new namespace was created, remind the user to fill out the namespace README (`display_name`, `bio`, `status`, `github`, etc.) and replace the placeholder avatar. Note that this is only needed if they plan to contribute to the registry. +- If any icons were referenced but not found, list them and note they need to be sourced and added to both this repo's `.icons/` directory and the `coder/coder` repo at `site/static/icon/`. +- A note that to contribute the module to the public registry, they can open a pull request to . diff --git a/.claude/skills/coder-templates/SKILL.md b/.claude/skills/coder-templates/SKILL.md index 247d06479..d9c822eb1 100644 --- a/.claude/skills/coder-templates/SKILL.md +++ b/.claude/skills/coder-templates/SKILL.md @@ -19,6 +19,8 @@ Before writing or modifying any code: When updating an existing template, read and understand all of its current resources, parameters, and module consumption before making changes. If you observe patterns that deviate from the coder template standards (e.g. missing metadata blocks, hardcoded values that should be parameters, inline implementations that existing modules could replace, missing error handling in scripts), note these to the user as improvement opportunities in your response. +Always prefer the proper implementation over a simpler shortcut. Templates are infrastructure that users depend on. Doing less work is not the same as reducing complexity if it leaves the template incomplete or fragile. + Features marked as "Premium" in this skill require a Coder Premium license. When your implementation uses a Premium feature, note this in your response to the user so they can verify their deployment supports it. ## Documentation References @@ -30,6 +32,7 @@ Features marked as "Premium" in this skill require a Coder Premium license. When - Creating templates: - Extending templates: - Template parameters: +- Dynamic parameters: - Workspace presets: - Prebuilt workspaces: - Tasks: @@ -88,11 +91,11 @@ From repo root: ./scripts/new_template.sh namespace/template-name ``` -Names must be lowercase alphanumeric with hyphens or underscores (e.g. `my-org/python-docker`). +Names must be lowercase alphanumeric with hyphens or underscores (e.g. `my-org/aws-ec2`). Creates `registry//templates//` with: -- `main.tf`: full workspace Terraform config +- `main.tf`: full workspace Terraform config with common patterns — read this as the primary reference for template structure - `README.md`: frontmatter and documentation If the namespace is new, the script also creates `registry//` with a README. New namespaces additionally need: @@ -100,126 +103,14 @@ If the namespace is new, the script also creates `registry//` with a - `registry//.images/avatar.svg` (or `.png`): square image, 400x400px minimum - The namespace README `avatar` field pointing to `./.images/avatar.svg` -The generated namespace README contains placeholder fields (`display_name`, `bio`, `status`, `github`, `avatar`, etc.) that the user must fill out. The `status` field is required and must be `official`, `partner`, or `community` (typically `community` for new contributors). After completing the template, inform the user that the namespace README needs to be updated with their information. - -## main.tf - -Templates define the full workspace stack: providers, agent, infrastructure resources, and module consumption. - -```tf -terraform { - required_providers { - coder = { - source = "coder/coder" - } - docker = { - source = "kreuzwerker/docker" - } - } -} - -data "coder_provisioner" "me" {} -data "coder_workspace" "me" {} -data "coder_workspace_owner" "me" {} - -resource "coder_agent" "main" { - arch = data.coder_provisioner.me.arch - os = "linux" - startup_script = <<-EOT - set -e - if [ ! -f ~/.init_done ]; then - cp -rT /etc/skel ~ - touch ~/.init_done - fi - EOT - - 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}" - } - - metadata { - display_name = "CPU Usage" - key = "cpu" - script = "coder stat cpu" - interval = 10 - timeout = 1 - } - - metadata { - display_name = "RAM Usage" - key = "memory" - script = "coder stat mem" - interval = 10 - timeout = 1 - } - - metadata { - display_name = "Home Disk" - key = "disk" - script = "coder stat disk --path $${HOME}" - interval = 60 - timeout = 1 - } - -} - -module "code-server" { - count = data.coder_workspace.me.start_count - source = "registry.coder.com/coder/code-server/coder" - version = "~> 1.0" - agent_id = coder_agent.main.id -} - -resource "docker_volume" "home_volume" { - name = "coder-${data.coder_workspace.me.id}-home" - lifecycle { - ignore_changes = all - } - labels { - label = "coder.owner" - value = data.coder_workspace_owner.me.name - } - labels { - label = "coder.workspace_id" - value = data.coder_workspace.me.id - } -} +The scaffolding script does not create the `.images/` directory or avatar file. When a new namespace is created, create `registry//.images/` and add a placeholder `avatar.svg` so the directory structure is ready for the user to replace with their real avatar. -resource "docker_container" "workspace" { - count = data.coder_workspace.me.start_count - image = "codercom/enterprise-base:ubuntu" - name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}" - hostname = data.coder_workspace.me.name - entrypoint = ["sh", "-c", replace(coder_agent.main.init_script, "/localhost|127\\.0\\.0\\.1/", "host.docker.internal")] - env = ["CODER_AGENT_TOKEN=${coder_agent.main.token}"] - host { - host = "host.docker.internal" - ip = "host-gateway" - } - volumes { - container_path = "/home/coder" - volume_name = docker_volume.home_volume.name - read_only = false - } - labels { - label = "coder.owner" - value = data.coder_workspace_owner.me.name - } - labels { - label = "coder.workspace_id" - value = data.coder_workspace.me.id - } -} -``` +The generated namespace README contains placeholder fields (`display_name`, `bio`, `status`, `github`, `avatar`, etc.) that the user must fill out. The `status` field is required and must be `official`, `partner`, or `community` (typically `community` for new contributors). -Key patterns: +## Key Patterns - Provider version constraints must reflect actual functionality requirements. Only set a minimum `coder` provider version when the template uses a resource, attribute, or behavior introduced in that version. The same applies to infrastructure providers (Docker, AWS, etc.); check provider changelogs to confirm. - Include `data.coder_workspace.me` and `data.coder_workspace_owner.me` for workspace and owner metadata. Include `data.coder_provisioner.me` only when you need the provisioner's `arch` or `os` for `coder_agent` (typical for Docker, Kubernetes, Incus); omit when the workspace OS/arch is fixed (e.g. cloud VMs with a known image). -- Use `data "coder_parameter"` for user-facing knobs. For new templates, include standard options for the platform (e.g. region, CPU, memory, disk size for cloud/VM templates); align with same-platform templates in `registry/` when available, otherwise use the registry website. Expose stated preferences as the parameter `default` with additional sensible `option` values unless the user explicitly restricts that dimension. - Use `locals {}` for computed values: username, environment variables, startup scripts, URL assembly - Use `data.coder_workspace.me.start_count` as `count` on ephemeral resources - Connect containers/VMs to the agent via `coder_agent.main.init_script` and `CODER_AGENT_TOKEN` @@ -242,6 +133,18 @@ Templates can include files beyond `main.tf` + `README.md`: - `build/Dockerfile`: custom container images built by the template - `.tftpl` files: any Terraform template files for scripts, configs, or cloud-init data +### Parameters + +Use `data "coder_parameter"` for user-facing workspace options. Typical parameters: region/instance type/CPU/memory/disk for cloud VMs; container image or runtime version for Docker (pass as `build_arg` when using a local Dockerfile). Use same-platform templates in `registry/` as a starting reference, not a rigid pattern. Expose stated preferences as the parameter `default` with additional sensible `option` values unless the user explicitly restricts it. + +- Prefer `dynamic "option"` blocks with `for_each` from a `locals` map over static `option` blocks. See the region selector modules (e.g. `coder/aws-region`) for the pattern. +- Use `form_type` for richer UI controls: `dropdown` (searchable), `multi-select` (for `list(string)`), `slider` (numeric), `radio`, `checkbox`, `textarea`. +- Conditional parameters: use `count` to show/hide a parameter based on another parameter's value. +- `mutable = false` for infrastructure that can't change after creation (region, disk); `mutable = true` for runtime config. +- `ephemeral = true` for one-shot build options that don't persist between starts. +- `validation {}` with `min`/`max`/`monotonic` for numbers, `regex`/`error` for strings. +- Dynamic parameter features require Coder provider `>= 2.4.0`. + ### Presets Workspace presets bundle commonly-used parameter combinations into selectable options. When a user creates a workspace, they can pick a preset to auto-fill multiple parameters at once. Define presets with `data "coder_workspace_preset"`: @@ -374,20 +277,19 @@ Workflow: 2. **Use existing icons when they fit.** Most templates use a platform icon (aws, gcp, azure, docker, kubernetes) that already exists. 3. **When an icon doesn't exist,** reference the expected path anyway so the structure is correct. Try to source the official SVG from the platform's branding page or repository. If you can obtain it, add it to `.icons/` in this repo. 4. **Don't substitute a generic icon.** If the platform has its own brand identity, use the correct name even if the file doesn't exist yet. -5. **Notify the user.** After completing the template, inform the user in your response if any icons were referenced but not found. Note that missing icons need to be sourced and added to both this repo's `.icons/` directory and the `coder/coder` repo at `site/static/icon/`. +5. **Track missing icons** so you can report them in your response. ## Testing -Templates do NOT require `.tftest.hcl` or `main.test.ts`. Testing is done by pushing the template to a Coder deployment with `coder templates push`. +Templates do NOT require `.tftest.hcl` or `main.test.ts`. Testing is done by pushing the template to a Coder deployment. ## Commands -| Task | Command | Scope | -| --------------- | ------------------------------------------------------- | ----- | -| Format all | `bun run fmt` | Repo | -| Validate | `./scripts/terraform_validate.sh` | Repo | -| ShellCheck | `bun run shellcheck` | Repo | -| README validate | `go build ./cmd/readmevalidation && ./readmevalidation` | Repo | +| Task | Command | Scope | +| ---------- | --------------------------------- | ----- | +| Format all | `bun run fmt` | Repo | +| Validate | `./scripts/terraform_validate.sh` | Repo | +| ShellCheck | `bun run shellcheck` | Repo | ## Final Checks @@ -396,8 +298,24 @@ Before considering the work complete, verify: - `terraform init && terraform validate` passes in the template directory - `bun run fmt` has been run - `bun run shellcheck` passes if the template includes shell scripts -- `go build ./cmd/readmevalidation && ./readmevalidation` passes (validates frontmatter, icon paths, body structure, GFM alerts) - README documents prerequisites and architecture - Shell scripts handle errors gracefully (`|| echo "Warning..."` for non-fatal failures). If a script sources external files (`$HOME/.bashrc`, `/etc/bashrc`, `/etc/os-release`), the `source` must come before `set -u`; CI enforces this ordering. - No hardcoded values that should be configurable via variables or parameters - Asset and icon paths in frontmatter and Terraform must be relative (e.g. `../../../../.icons/`), not absolute. External hyperlinks to docs or other websites are fine. + +## Response to the User + +In your response, include: + +- A ready-to-run push command with real values filled in. Use `-d` to point at the template directory (so it works from the repo root), `-m` for a short description, and `-y` to skip interactive prompts: + +```bash +coder templates push \ + registry/ \ + -m "Initial version: " \ + -y < template-name > -d < namespace > /templates/ < template-name > / +``` + +- If a new namespace was created, remind the user to fill out the namespace README (`display_name`, `bio`, `status`, `github`, etc.) and replace the placeholder avatar. Note that this is only needed if they plan to contribute to the registry. +- If any icons were referenced but not found, list them and note they need to be sourced and added to both this repo's `.icons/` directory and the `coder/coder` repo at `site/static/icon/`. +- A note that to contribute the template to the public registry, they can open a pull request to . From 98a56612351d0d8e05f48a3fe8069c1a4a0100cd Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Tue, 31 Mar 2026 10:55:58 -0500 Subject: [PATCH 10/11] chore: underscores not allowed --- .claude/skills/coder-modules/SKILL.md | 2 +- .claude/skills/coder-templates/SKILL.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.claude/skills/coder-modules/SKILL.md b/.claude/skills/coder-modules/SKILL.md index 94c347d95..8b461ff44 100644 --- a/.claude/skills/coder-modules/SKILL.md +++ b/.claude/skills/coder-modules/SKILL.md @@ -59,7 +59,7 @@ From repo root: ./scripts/new_module.sh namespace/module-name ``` -Names must be lowercase alphanumeric with hyphens or underscores (e.g. `coder/my-tool`). +Names must be lowercase alphanumeric with hyphens (e.g. `coder/my-tool`). Underscores are not allowed. Creates `registry//modules//` with: diff --git a/.claude/skills/coder-templates/SKILL.md b/.claude/skills/coder-templates/SKILL.md index d9c822eb1..9edd1f6f8 100644 --- a/.claude/skills/coder-templates/SKILL.md +++ b/.claude/skills/coder-templates/SKILL.md @@ -91,7 +91,7 @@ From repo root: ./scripts/new_template.sh namespace/template-name ``` -Names must be lowercase alphanumeric with hyphens or underscores (e.g. `my-org/aws-ec2`). +Names must be lowercase alphanumeric with hyphens (e.g. `my-org/aws-ec2`). Underscores are not allowed. Creates `registry//templates//` with: From 9f8d1af65c6a192ad1b92b7a0299c8873cbf2b12 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Tue, 31 Mar 2026 12:42:50 -0500 Subject: [PATCH 11/11] chore: amove skills to .agents/skills and symlink for claude like AGENTS.md --- {.claude => .agents}/skills/coder-modules/SKILL.md | 0 {.claude => .agents}/skills/coder-templates/SKILL.md | 0 .claude/skills | 1 + 3 files changed, 1 insertion(+) rename {.claude => .agents}/skills/coder-modules/SKILL.md (100%) rename {.claude => .agents}/skills/coder-templates/SKILL.md (100%) create mode 120000 .claude/skills diff --git a/.claude/skills/coder-modules/SKILL.md b/.agents/skills/coder-modules/SKILL.md similarity index 100% rename from .claude/skills/coder-modules/SKILL.md rename to .agents/skills/coder-modules/SKILL.md diff --git a/.claude/skills/coder-templates/SKILL.md b/.agents/skills/coder-templates/SKILL.md similarity index 100% rename from .claude/skills/coder-templates/SKILL.md rename to .agents/skills/coder-templates/SKILL.md diff --git a/.claude/skills b/.claude/skills new file mode 120000 index 000000000..2b7a412b8 --- /dev/null +++ b/.claude/skills @@ -0,0 +1 @@ +../.agents/skills \ No newline at end of file