# Gen Devcontainer

> Generate a `.devcontainer/devcontainer.json` keyed to the project's primary language so `gh codespace create` (or VS Code's "Reopen in Container") gives anyone on the team a ready-to-go env with the right runtime, GH CLI, git-lfs, and language-tier VS Code extensions pre-installed. Preview-by-default; idempotent on apply (no-op on identical bytes, diff-and-refuse on divergence unless `--force-overwrite`). TRIGGER when the user says "add a devcontainer", "set up codespaces", "scaffold devcontainer.json", "reopen in container", "configure dev environment", "containerized dev env", "make this work in codespaces", "add VS Code container", "wire up devcontainer", "/nyann:gen-devcontainer". Also trigger when a user asks "how do I run this in Codespaces" and there's no `.devcontainer/` directory yet. Do NOT trigger on "build a Docker image for production" — that's a Dockerfile concern outside devcontainer scope. Do NOT trigger on "fix my devcontainer build" — that's debugging an existing setup, not generating one (r

- Skill: `thettwe/gen-devcontainer` (Agent Skill)
- Install (CLI): `npx skillmds@latest add thettwe/gen-devcontainer`
- Raw SKILL.md: https://api.skillmd.com/api/skills/thettwe/gen-devcontainer/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: thettwe (https://skillmd.com/u/thettwe)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/thettwe/gen-devcontainer

---


# gen-devcontainer

Wraps `bin/gen-devcontainer.sh`. Emits a devcontainer.json; preview-
by-default; idempotent on apply.

## When to trigger

- User wants a Codespaces-ready repo and there's no `.devcontainer/`
  directory yet.
- User wants to refresh an existing devcontainer.json after upgrading
  nyann (base image tags + extension lists shift with the
  `snapshot_version` comment in the file).
- User wants to add forwarded ports / cpus / memory to an existing
  devcontainer (use `--port 3000 --cpus 4 --memory 8gb`).

## When NOT to trigger

- User wants a **production Dockerfile** — that's an entirely
  different concern; devcontainer.json is dev-environment only.
- User wants to **debug** a broken devcontainer build — open the
  existing file with them; this skill generates fresh, not diagnoses.
- User is on a non-Codespaces, non-VS Code editor — the file still
  works under JetBrains' Dev Containers plugin and `devcontainer
  open` (CLI), but flag the broader VS Code-first defaults.

## Picking the language

Read `StackDescriptor.primary_language` from `bin/detect-stack.sh`:

| StackDescriptor.primary_language | --language |
|---|---|
| `typescript` / `javascript` | `node` |
| `python` | `python` |
| `go` | `go` |
| `rust` | `rust` |
| `dart` | `dart` |
| `java` / `kotlin` | `java` |
| `csharp` / `dotnet` | `dotnet` |
| `php` | `php` |
| `ruby` | `ruby` |
| `swift` | `swift` |
| `elixir` | `elixir` |
| `cpp` / `c` | `cpp` |

For polyglot monorepos (`workspaces[]` present), pick the language of
the most-important workspace OR ask the user — running multiple
devcontainers from a single repo is uncommon and confusing.

## Preview, then apply

Always preview first. Show the rendered JSON; wait for confirmation:

```
bin/gen-devcontainer.sh --language python --name my-app
```

Then on confirmation:

```
bin/gen-devcontainer.sh --language python --name my-app \
  --target . --apply
```

If the destination already matches: log "unchanged" and exit. If it
differs: print the unified diff to stderr, exit 3, prompt the user
before re-running with `--force-overwrite`.

## Output shape

`.devcontainer/devcontainer.json` containing:

- `$schema` pointing at the official devcontainer spec
- `$comment` tagging the nyann snapshot version
- `name` (display name in 'Open in Container' picker)
- `image` (per-language, pinned to a major:minor tag)
- `features` (gh CLI, git-lfs, common-utils — version-pinned)
- `customizations.vscode.extensions` (per-language pack + gitlens + git-graph)
- `postCreateCommand` (per-language dep install — `npm ci`, `uv sync`, `cargo fetch`, etc.)
- `forwardPorts` (when `--port` flag(s) supplied)
- `hostRequirements` (when `--cpus` / `--memory` / `--storage` supplied)

## Defaults explained

- **Base image**: Microsoft devcontainers/<lang> images where they
  exist (well-maintained, security-scanned, multi-arch). Vendor
  images for Dart / Swift / Elixir where no MS variant exists.
- **Image tag**: pinned to `major:minor`, NOT a digest. Operators who
  need byte-reproducible builds should pin to a digest by hand; the
  Docker ecosystem of `gen-dependency-updater` can roll the tag.
- **Extensions**: per-language language pack + `gitlens` + `git-graph`
  baseline. Conservative pick; extend via `--extension`.
- **Features**: `github-cli:1` + `git-lfs:1` + `common-utils:2`,
  always with a version pin so floating tags can't quietly re-broadcast.
- **postCreateCommand**: best-effort install that no-ops when the
  expected manifest is missing (so the file works in a partially-
  scaffolded repo). Override with `--post-create-command` for non-
  standard package managers (e.g. pnpm rather than npm).

## Reading the output back

When previewing:
- Surface the base image (the operator may want to swap to an
  org-internal mirror).
- Surface the postCreateCommand (so they can spot install commands
  that don't fit their workflow).
- Suggest `--port` flags if the user mentioned a dev-server port in
  the conversation.

When applying:
- Confirm the dest path didn't already exist (or that the diff was
  intentional if `--force-overwrite` was used).
- Suggest a follow-up `gh codespace create` invocation if the user
  asked for Codespaces specifically.

