Catalogs
The curated base images, devcontainer features and suggested extensions a project picker offers before anyone types anything.
Why it exists
Data, not logic — but shared data, because the alternative is what already happened: two
hand-maintained copies that drifted. Spunto Lite's copy was missing java,
kubectl-helm-minikube and google-cloud-cli, and described common-utils as installing Oh My
Zsh at a time when the image recipe explicitly passes installOhMyZsh: false. Nobody decided
either of those; they are the shape of a catalog nobody owns.
The catalogs are also load-bearing for the generators: buildImageScript in
./script resolves a selected feature id through
AVAILABLE_FEATURES to find its OCI ref, its default options or its local script.
import { AVAILABLE_IMAGES, AVAILABLE_FEATURES, SUGGESTED_EXTENSIONS } from "@spunto/build/catalogs"Project templates are deliberately absent
Spunto Cloud's templates cloned a starter repository and carried onboarding-gallery presentation (logo, accent, category); Lite's only configure an environment. They answer different questions and diverge on purpose, so they fail the test for being in this package.
API
DevImage and AVAILABLE_IMAGES
type DevImage = { id: string; label: string; image: string; description: string }
const AVAILABLE_IMAGES: DevImage[]The base images a project can start from:
id | label | image |
|---|---|---|
typescript-node-20 | Node.js 20 (TypeScript) | mcr.microsoft.com/devcontainers/typescript-node:20 |
javascript-node-20 | Node.js 20 (JavaScript) | mcr.microsoft.com/devcontainers/javascript-node:20 |
python-312 | Python 3.12 | mcr.microsoft.com/devcontainers/python:3.12 |
go-121 | Go 1.21 | mcr.microsoft.com/devcontainers/go:1.21 |
rust | Rust | mcr.microsoft.com/devcontainers/rust:latest |
java-21 | Java 21 | mcr.microsoft.com/devcontainers/java:21 |
DevFeature, DevFeatureOption and AVAILABLE_FEATURES
type DevFeatureOption = { name: string; default: string; description: string }
const AVAILABLE_FEATURES: DevFeature[]Prop
Type
id | Source | User options | Notes |
|---|---|---|---|
docker-in-docker | ghcr.io/coderhammer/features/docker-in-docker:3 | version (latest) | Fork of the upstream feature, which breaks when a buildx/compose tag is pushed before its release asset. defaultOptions: { moby: "false" } (moby-cli is unavailable on Debian trixie and newer). |
claude-code | ghcr.io/stu-bell/devcontainer-features/claude-code:0 | — | |
node | ghcr.io/devcontainers/features/node:1 | version (lts) | |
python | ghcr.io/devcontainers/features/python:1 | version (latest) | |
go | ghcr.io/devcontainers/features/go:1 | version (latest) | |
rust | ghcr.io/devcontainers/features/rust:1 | version (latest) | |
java | ghcr.io/devcontainers/features/java:1 | version (latest) | |
aws-cli | ghcr.io/devcontainers/features/aws-cli:1 | — | |
github-cli | ghcr.io/devcontainers/features/github-cli:1 | — | |
kubectl-helm-minikube | ghcr.io/devcontainers/features/kubectl-helm-minikube:1 | — | |
terraform | ghcr.io/devcontainers/features/terraform:1 | — | |
google-cloud-cli | local script google-cloud-cli.sh | — | Installed from Google's own apt repository. |
common-utils | ghcr.io/devcontainers/features/common-utils:2 | — | Already in every image (it creates the vscode user). Listed so a project can override its options; they are merged over the platform's rather than installed twice. |
// A feature picker with option inputs
for (const f of AVAILABLE_FEATURES) {
renderFeature(f.label, f.description, f.options ?? [])
}
// Look up what a stored selection means
const node = AVAILABLE_FEATURES.find((f) => f.id === "node")
node?.options?.[0] // → { name: "version", default: "lts", description: "Node.js version (lts, 20, 22, etc.)" }A project may also store a feature that is not in this catalog, by giving it an explicit
ociRef (a hand-typed OCI reference). The generators install it from that ref.
SUGGESTED_EXTENSIONS
const SUGGESTED_EXTENSIONS: ExtensionSuggestion[]The default state of an extension picker, shown before anyone types a query:
esbenp.prettier-vscode, dbaeumer.vscode-eslint, ms-python.python, golang.go,
rust-lang.rust-analyzer, bradlc.vscode-tailwindcss, eamodio.gitlens,
ms-azuretools.vscode-docker.
Entries carry id, label and publisher only — no registry URLs. The catalog is
registry-agnostic: attach links for whichever registry is active with withRegistryUrls from
./extensions.
import { parseGallery, withRegistryUrls } from "@spunto/build/extensions"
const defaults = withRegistryUrls(parseGallery(org.extensionsGallery), SUGGESTED_EXTENSIONS)The bar for being listed here is being published on Open VSX — the default registry, the one
every organization gets with no configuration — so the defaults stay installable without extra
setup. Check before adding one: curl -s https://open-vsx.org/api/<publisher>/<name>; a 404 means
it does not belong here.
Scripts
Turning a project's spec into the shell a worker runs — image build, first boot, every boot, the container CMD, the agent-orchestrated plan, and organization skills.
Types
The canonical shapes a worker's environment is described by, the status it reports back, and the worker status vocabulary with its transition graph.