From 898b4f0fa4381174672e4ff9c101cee4eb80f48c Mon Sep 17 00:00:00 2001 From: Serge Gatezh <2880401+gatezh@users.noreply.github.com> Date: Tue, 6 Oct 2026 13:42:16 -0600 Subject: [PATCH 1/2] chore: drop devcontainer comments that restate the config Per-extension descriptions repeat the extension ID, and many setting and mount comments repeat the key below them. Keep the ones that say why, give an issue number, or tell a template adopter what to change. The devcontainer.json rule now says so, since it was the source of the per-extension descriptions. --- .claude/rules/devcontainer.md | 7 ++- .../claude-sandbox/devcontainer.json | 9 --- .devcontainer/claude-sandbox/init-firewall.sh | 3 - .devcontainer/devcontainer.json | 14 ----- bun/.devcontainer/devcontainer.json | 8 --- claude-bun/.devcontainer/devcontainer.json | 15 +---- .../claude-sandbox/devcontainer.json | 63 +++++-------------- claude-code/.devcontainer/devcontainer.json | 61 +++++------------- claude-code/.devcontainer/init-plugins.sh | 1 - hugo-bun-node/.devcontainer/devcontainer.json | 11 ---- hugo-bun/.devcontainer/devcontainer.json | 11 ---- 11 files changed, 36 insertions(+), 167 deletions(-) diff --git a/.claude/rules/devcontainer.md b/.claude/rules/devcontainer.md index c34a46f..6e81f4e 100644 --- a/.claude/rules/devcontainer.md +++ b/.claude/rules/devcontainer.md @@ -12,6 +12,10 @@ paths: // README at: {relevant-reference-url} ``` +## Comments + +Comment only what the JSON can't say: a reason, a constraint, an issue number, or what a template adopter must change. A comment that restates the key below it (`// Show workspace folder name in window title` above `"window.title"`) is noise; leave it out. + ## node_modules Mount Always include to keep node_modules off the host: @@ -24,12 +28,11 @@ Always include to keep node_modules off the host: ## VS Code Extensions -Group by category with header comments: +Group by category with a header comment. No per-extension description: the ID names the extension. ```jsonc "extensions": [ // **Category Name** - // Extension Description "publisher.extension-id" ] ``` diff --git a/.devcontainer/claude-sandbox/devcontainer.json b/.devcontainer/claude-sandbox/devcontainer.json index c62ed6d..3fcffac 100644 --- a/.devcontainer/claude-sandbox/devcontainer.json +++ b/.devcontainer/claude-sandbox/devcontainer.json @@ -33,25 +33,18 @@ "bash": { "path": "bash", "icon": "terminal-bash" } }, "extensions.ignoreRecommendations": true, - // ── Formatter settings (customize per project) ────────────────── - // Change "editor.defaultFormatter" to match your tooling: - // Biome: "biomejs.biome" | Prettier: "esbenp.prettier-vscode" - // OXC: "oxc.oxc-vscode" | None: remove these three settings "editor.formatOnSave": true, "editor.defaultFormatter": "oxc.oxc-vscode", "editor.codeActionsOnSave": { "source.fixAll": "explicit", "source.organizeImports": "explicit" }, - // Show workspace folder name in window title "window.title": "${localWorkspaceFolderBasename}", - // Sandbox visual identity — Claude Dark theme with coral remote indicator "workbench.colorTheme": "Claude Dark", "workbench.colorCustomizations": { "statusBarItem.remoteBackground": "#C15F3C", "statusBarItem.remoteForeground": "#ffffff" }, - // Allow Claude Code to skip permission prompts in sandbox "claudeCode.allowDangerouslySkipPermissions": true } } @@ -59,7 +52,6 @@ "mounts": [ "source=devcontainers-sandbox-config-${devcontainerId},target=/home/node/.claude,type=volume", "source=devcontainers-sandbox-zsh-history-${devcontainerId},target=/commandhistory,type=volume", - // Mount firewall script into the expected path "source=${localWorkspaceFolder}/.devcontainer/claude-sandbox/init-firewall.sh,target=/usr/local/bin/init-firewall.sh,type=bind" ], "containerEnv": { @@ -71,7 +63,6 @@ }, // Plugins before firewall (network still open during postCreateCommand) "postCreateCommand": "sudo chown -R node /home/node/.claude /commandhistory && bash /workspace/.devcontainer/init-plugins.sh", - // Firewall locks down the network "postStartCommand": "sudo /usr/local/bin/init-firewall.sh", "waitFor": "postStartCommand" } diff --git a/.devcontainer/claude-sandbox/init-firewall.sh b/.devcontainer/claude-sandbox/init-firewall.sh index 6f98f4d..44f1c15 100755 --- a/.devcontainer/claude-sandbox/init-firewall.sh +++ b/.devcontainer/claude-sandbox/init-firewall.sh @@ -5,7 +5,6 @@ IFS=$'\n\t' # 1. Extract Docker DNS info BEFORE any flushing DOCKER_DNS_RULES=$(iptables-save -t nat | grep "127\.0\.0\.11" || true) -# Flush existing rules and delete existing ipsets iptables -F iptables -X iptables -t nat -F @@ -32,10 +31,8 @@ iptables -A INPUT -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT iptables -A INPUT -i lo -j ACCEPT iptables -A OUTPUT -o lo -j ACCEPT -# Create ipset with CIDR support ipset create allowed-domains hash:net -# Fetch GitHub IP ranges echo "Fetching GitHub IP ranges..." gh_ranges=$(curl -s https://api.github.com/meta) if [ -z "$gh_ranges" ]; then diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 931e2f7..0c2c64f 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -15,17 +15,12 @@ "vscode": { "extensions": [ // **AI Agents** - // Claude Code - AI assistant "anthropic.claude-code", // **General** - // YAML - YAML support (for workflows and docker-compose) "redhat.vscode-yaml", - // Markdown Preview Github Styles - Markdown preview "bierner.markdown-preview-github-styles", - // Markdown Preview Mermaid Support - Mermaid diagrams "bierner.markdown-mermaid", - // Docker - Dockerfile support "ms-azuretools.vscode-docker" ], "settings": { @@ -34,21 +29,14 @@ "zsh": { "path": "zsh" }, "bash": { "path": "bash", "icon": "terminal-bash" } }, - // Suppress extension recommendation prompts "extensions.ignoreRecommendations": true, - // ── Formatter settings (customize per project) ────────────────── - // Change "editor.defaultFormatter" to match your tooling: - // Biome: "biomejs.biome" | Prettier: "esbenp.prettier-vscode" - // OXC: "oxc.oxc-vscode" | None: remove these three settings "editor.formatOnSave": true, "editor.defaultFormatter": "oxc.oxc-vscode", "editor.codeActionsOnSave": { "source.fixAll": "explicit", "source.organizeImports": "explicit" }, - // Show workspace folder name in window title "window.title": "${localWorkspaceFolderBasename}", - // Visual identity — Claude Dark theme with coral remote indicator "workbench.colorTheme": "Claude Dark", "workbench.colorCustomizations": { "statusBarItem.remoteBackground": "#C15F3C", @@ -58,9 +46,7 @@ } }, "mounts": [ - // Persist Claude Code configuration between container rebuilds "source=devcontainers-claude-config-${devcontainerId},target=/home/node/.claude,type=volume", - // Persist zsh history between container rebuilds "source=devcontainers-zsh-history-${devcontainerId},target=/commandhistory,type=volume" ], "containerEnv": { diff --git a/bun/.devcontainer/devcontainer.json b/bun/.devcontainer/devcontainer.json index 20aa935..88cbe9b 100644 --- a/bun/.devcontainer/devcontainer.json +++ b/bun/.devcontainer/devcontainer.json @@ -11,17 +11,12 @@ "source=${localWorkspaceFolderBasename}-node_modules,target=${containerWorkspaceFolder}/node_modules,type=volume" ], - // Configure tool-specific properties. "customizations": { - // Configure properties specific to VS Code. "vscode": { - // Use zsh as the default shell for better VS Code integration "settings": { "terminal.integrated.defaultProfile.linux": "zsh", - // Suppress extension recommendation prompts (e.g., Container Tools in Codespaces) "extensions.ignoreRecommendations": true }, - // Add the IDs of extensions you want installed when the container is created. "extensions": [ // **Claude Code** "anthropic.claude-code", @@ -29,13 +24,10 @@ // **Bun** "oven.bun-vscode", - // Prettier - Code Formatter "esbenp.prettier-vscode", // **Tailwind** - // Tailwind CSS IntelliSense "bradlc.vscode-tailwindcss", - // Tailwind Fold "stivo.tailwind-fold" ] } diff --git a/claude-bun/.devcontainer/devcontainer.json b/claude-bun/.devcontainer/devcontainer.json index a4bb0f0..6a27376 100644 --- a/claude-bun/.devcontainer/devcontainer.json +++ b/claude-bun/.devcontainer/devcontainer.json @@ -18,18 +18,13 @@ "mounts": [ // Keep node_modules out of a host machine "source=${localWorkspaceFolderBasename}-node_modules,target=${containerWorkspaceFolder}/node_modules,type=volume", - // Persist shell history between container rebuilds. zsh writes here - // (HISTFILE in the Dockerfile); the volume name is legacy. + // zsh writes its history here (HISTFILE in the Dockerfile); the volume name is legacy. "source=claude-code-bashhistory-${devcontainerId},target=/commandhistory,type=volume", - // Persist Claude Code configuration between container rebuilds "source=claude-code-config-${devcontainerId},target=/home/bun/.claude,type=volume" ], - // Configure tool-specific properties. "customizations": { - // Configure properties specific to VS Code. "vscode": { - // Use zsh as the default shell for better VS Code integration "settings": { "terminal.integrated.defaultProfile.linux": "zsh", "terminal.integrated.profiles.linux": { @@ -41,18 +36,14 @@ "path": "zsh" } }, - // Suppress extension recommendation prompts (e.g., Container Tools in Codespaces) "extensions.ignoreRecommendations": true, - // Format on save with Biome "editor.formatOnSave": true, "editor.defaultFormatter": "biomejs.biome", - // Biome auto-fix on save "editor.codeActionsOnSave": { "source.fixAll.biome": "explicit", "source.organizeImports.biome": "explicit" } }, - // Add the IDs of extensions you want installed when the container is created. "extensions": [ // **Claude Code** "anthropic.claude-code", @@ -61,13 +52,10 @@ "oven.bun-vscode", // **Code Quality** - // Biome - Fast formatter and linter for JavaScript/TypeScript "biomejs.biome", // **Tailwind** - // Tailwind CSS IntelliSense "bradlc.vscode-tailwindcss", - // Tailwind Fold "stivo.tailwind-fold" ] } @@ -81,7 +69,6 @@ }, "workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=delegated", "workspaceFolder": "/workspace", - // Initialize firewall after container starts "postStartCommand": "sudo /usr/local/bin/init-firewall.sh", "waitFor": "postStartCommand" } diff --git a/claude-code/.devcontainer/claude-sandbox/devcontainer.json b/claude-code/.devcontainer/claude-sandbox/devcontainer.json index bc7c1ad..b00c2cd 100644 --- a/claude-code/.devcontainer/claude-sandbox/devcontainer.json +++ b/claude-code/.devcontainer/claude-sandbox/devcontainer.json @@ -4,13 +4,9 @@ "dockerComposeFile": "docker-compose.yml", "service": "devcontainer", "workspaceFolder": "/workspace", - // Pull the prebuilt image on the HOST before the build, so "Rebuild Without - // Cache" always layers on the freshest base. Replaces `pull_policy: always` in - // docker-compose.yml, which breaks on Linux/WSL2 when `updateRemoteUserUID` - // builds a local-only image (issue #109). `|| exit 0` keeps offline/outage opens - // working (valid in POSIX sh and Windows cmd.exe). Keep in sync with the compose image. + // initializeCommand: see the default variant. "initializeCommand": "docker pull ghcr.io/gatezh/devcontainers/claude-code-sandbox:latest || exit 0", - // Capabilities required for iptables firewall setup + // For the iptables firewall. "capAdd": ["NET_ADMIN", "NET_RAW"], "init": true, "updateRemoteUserUID": true, @@ -20,59 +16,42 @@ "vscode": { "extensions": [ // **AI Agents** - // Claude Code - AI coding assistant with chat, inline edits, and terminal integration "anthropic.claude-code", // **Runtime** - // Bun - Bun runtime support (debugging, lockfile viewer, bundler integration) "oven.bun-vscode", // **Code Quality** - // OXC - Fast linter and formatter for JavaScript/TypeScript (Rust-based) "oxc.oxc-vscode", // **Tailwind** - // Tailwind CSS IntelliSense - autocomplete, syntax highlighting, linting for Tailwind classes "bradlc.vscode-tailwindcss", - // Tailwind Fold - collapse long Tailwind class strings in the editor for readability "stivo.tailwind-fold", // **General** - // YAML - YAML language support (for workflows and docker-compose) "redhat.vscode-yaml", - // Markdown Preview Github Styles - renders Markdown preview with GitHub's CSS "bierner.markdown-preview-github-styles", - // Markdown Preview Mermaid Support - renders Mermaid diagrams in Markdown preview "bierner.markdown-mermaid", - // Docker - Dockerfile and Compose syntax, linting, and image management "ms-azuretools.vscode-docker", // **Spelling** - // Code Spell Checker - catches typos in code and comments "streetsidesoftware.code-spell-checker", // **JavaScript / TypeScript** - // npm IntelliSense - autocomplete npm imports in import statements "christian-kohler.npm-intellisense", - // TypeScript Error Translator - human-readable TS error messages "mattpocock.ts-error-translator", // **CSS / Colors** - // Color Info - preview CSS colors on hover "bierner.color-info", - // Colorize - visualize CSS colors inline in the editor "kamikillerto.vscode-colorize", // **Frameworks** - // Drizzle ORM Snippets - code snippets for Drizzle ORM "drizzle-team.drizzle-orm-snippets", // **Testing** - // Playwright - run, debug, and generate Playwright tests from VS Code "ms-playwright.playwright", // **Sandbox Theme** - // Claude Theme - provides "Claude Dark" referenced by workbench.colorTheme "SamiHindi.claude-theme-sami-hindi" ], "settings": { @@ -89,36 +68,29 @@ "source.organizeImports": "explicit" }, "window.title": "${localWorkspaceFolderBasename}", - // Sandbox visual identity — Claude Dark theme with coral remote indicator + // Tells a sandbox window apart from the default variant at a glance. "workbench.colorTheme": "Claude Dark", "workbench.colorCustomizations": { "statusBarItem.remoteBackground": "#C15F3C", "statusBarItem.remoteForeground": "#ffffff" }, - // Allow Claude Code to skip permission prompts in sandbox "claudeCode.allowDangerouslySkipPermissions": true } } }, - // Named volumes keep node_modules OFF the host machine and persist across rebuilds. - // Uses ${localWorkspaceFolderBasename} so default and sandbox variants share volumes. - // Replace "myproject" with your project name (must match across both variants). + // Volume names end in ${localWorkspaceFolderBasename} so the default and sandbox + // variants share them. Replace "myproject" with your project name in both variants. "mounts": [ - // ── node_modules isolation (one per workspace) ───────────────────── - // Each workspace with a package.json needs its own volume mount. - // Without one, node_modules lands in the bind mount and shows up on the host. + // Every workspace with a package.json needs its own node_modules volume, or + // node_modules lands in the bind mount and shows up on the host. "source=myproject-node-modules-root-${localWorkspaceFolderBasename},target=/workspace/node_modules,type=volume", - // ── Monorepo: uncomment and customize for your structure ────────── // "source=myproject-node-modules-api-${localWorkspaceFolderBasename},target=/workspace/services/api/node_modules,type=volume", // "source=myproject-node-modules-web-${localWorkspaceFolderBasename},target=/workspace/apps/web/node_modules,type=volume", - // ── Persistent config ────────────────────────────────────────────── "source=myproject-claude-config-${localWorkspaceFolderBasename},target=/home/node/.claude,type=volume", "source=myproject-zsh-history-${localWorkspaceFolderBasename},target=/commandhistory,type=volume", - // gh CLI auth/state (~/.config/gh/hosts.yml) — keeps `gh auth login` across rebuilds "source=myproject-gh-config-${localWorkspaceFolderBasename},target=/home/node/.config/gh,type=volume", - // ── Firewall script ──────────────────────────────────────────────── - // The image provides iptables/ipset packages and sudo rule but NOT the script itself. - // Each project provides its own script via bind mount to customize the domain allowlist. + // The image has iptables, ipset and the sudo rule but not the script: each project + // mounts its own, with its own domain allowlist. "source=${localWorkspaceFolder}/.devcontainer/claude-sandbox/init-firewall.sh,target=/usr/local/bin/init-firewall.sh,type=bind" ], "containerEnv": { @@ -126,20 +98,15 @@ "DEVCONTAINER": "true", "NODE_OPTIONS": "--max-old-space-size=4096", "CLAUDE_CONFIG_DIR": "/home/node/.claude", - // Optional — only needed when the sandbox is used without the default variant, whose - // sign-in lands on the shared ~/.claude volume. Resolves to "" when the host var is - // unset. See "Sandbox Authentication" section in README. + // Only needed without the default variant, whose sign-in lands on the shared + // ~/.claude volume. See "Sandbox Authentication" in the README. "CLAUDE_CODE_OAUTH_TOKEN": "${localEnv:CLAUDE_CODE_OAUTH_TOKEN}" }, - // The find command chowns all node_modules volume mount points in one pass. - // Chromium is baked into the sandbox image (firewall blocks runtime install). - // bun install and init-plugins.sh are skipped when their file is absent. Plugins - // install here, before postStartCommand brings the firewall up. + // Plugins install here, before postStartCommand brings the firewall up (which is also + // why Chromium comes from the image). The chowns are the same safety net as in the + // default variant. "postCreateCommand": "sudo find /workspace -maxdepth 4 -name node_modules -type d -exec chown node {} + && sudo chown -R node /home/node/.claude /commandhistory /home/node/.config/gh && mise install && if [ -f package.json ]; then bun install; fi && if [ -f .devcontainer/init-plugins.sh ]; then bash .devcontainer/init-plugins.sh; fi", - // Firewall init (bind-mounted from project) + re-patch the Playwright MCP - // plugin's .mcp.json. The patch is defense in depth alongside the SessionStart - // hook in /etc/claude-code/managed-settings.json, which handles the case where - // the plugin auto-updates mid-container-run. See issues #85, #87, #98. + // patch-playwright-mcp: see the default variant. "postStartCommand": "sudo /usr/local/bin/init-firewall.sh && /usr/local/bin/patch-playwright-mcp", "waitFor": "postStartCommand" } diff --git a/claude-code/.devcontainer/devcontainer.json b/claude-code/.devcontainer/devcontainer.json index b505dca..a4e6f0b 100644 --- a/claude-code/.devcontainer/devcontainer.json +++ b/claude-code/.devcontainer/devcontainer.json @@ -4,11 +4,10 @@ "dockerComposeFile": "docker-compose.yml", "service": "devcontainer", "workspaceFolder": "/workspace", - // Pull the prebuilt image on the HOST before the build, so "Rebuild Without - // Cache" always layers on the freshest base. Replaces `pull_policy: always` in - // docker-compose.yml, which breaks on Linux/WSL2 when `updateRemoteUserUID` - // builds a local-only image (issue #109). `|| exit 0` keeps offline/outage opens - // working (valid in POSIX sh and Windows cmd.exe). Keep in sync with the compose image. + // Pulls on the HOST so "Rebuild Without Cache" layers on the freshest image. Compose's + // `pull_policy: always` breaks on Linux/WSL2, where `updateRemoteUserUID` builds a + // local-only image (#109). `|| exit 0` (valid in sh and cmd.exe) keeps offline opens + // working. Keep the image in sync with docker-compose.yml. "initializeCommand": "docker pull ghcr.io/gatezh/devcontainers/claude-code:latest || exit 0", "init": true, "updateRemoteUserUID": true, @@ -18,55 +17,39 @@ "vscode": { "extensions": [ // **AI Agents** - // Claude Code - AI coding assistant with chat, inline edits, and terminal integration "anthropic.claude-code", // **Runtime** - // Bun - Bun runtime support (debugging, lockfile viewer, bundler integration) "oven.bun-vscode", // **Code Quality** - // OXC - Fast linter and formatter for JavaScript/TypeScript (Rust-based) "oxc.oxc-vscode", // **Tailwind** - // Tailwind CSS IntelliSense - autocomplete, syntax highlighting, linting for Tailwind classes "bradlc.vscode-tailwindcss", - // Tailwind Fold - collapse long Tailwind class strings in the editor for readability "stivo.tailwind-fold", // **General** - // YAML - YAML language support (for workflows and docker-compose) "redhat.vscode-yaml", - // Markdown Preview Github Styles - renders Markdown preview with GitHub's CSS "bierner.markdown-preview-github-styles", - // Markdown Preview Mermaid Support - renders Mermaid diagrams in Markdown preview "bierner.markdown-mermaid", - // Docker - Dockerfile and Compose syntax, linting, and image management "ms-azuretools.vscode-docker", // **Spelling** - // Code Spell Checker - catches typos in code and comments "streetsidesoftware.code-spell-checker", // **JavaScript / TypeScript** - // npm IntelliSense - autocomplete npm imports in import statements "christian-kohler.npm-intellisense", - // TypeScript Error Translator - human-readable TS error messages "mattpocock.ts-error-translator", // **CSS / Colors** - // Color Info - preview CSS colors on hover "bierner.color-info", - // Colorize - visualize CSS colors inline in the editor "kamikillerto.vscode-colorize", // **Frameworks** - // Drizzle ORM Snippets - code snippets for Drizzle ORM "drizzle-team.drizzle-orm-snippets", // **Testing** - // Playwright - run, debug, and generate Playwright tests from VS Code "ms-playwright.playwright" ], "settings": { @@ -75,38 +58,29 @@ "zsh": { "path": "zsh" }, "bash": { "path": "bash", "icon": "terminal-bash" } }, - // Suppress extension recommendation prompts "extensions.ignoreRecommendations": true, - // ── Formatter settings (customize per project) ────────────────── - // Change "editor.defaultFormatter" to match your tooling: - // Biome: "biomejs.biome" | Prettier: "esbenp.prettier-vscode" - // OXC: "oxc.oxc-vscode" | None: remove these three settings + // Set the formatter to your project's tool (Biome "biomejs.biome", Prettier + // "esbenp.prettier-vscode"), or remove these three settings. "editor.formatOnSave": true, "editor.defaultFormatter": "oxc.oxc-vscode", "editor.codeActionsOnSave": { "source.fixAll": "explicit", "source.organizeImports": "explicit" }, - // Show workspace folder name in window title "window.title": "${localWorkspaceFolderBasename}" } } }, - // Named volumes keep node_modules OFF the host machine and persist across rebuilds. - // Uses ${localWorkspaceFolderBasename} so default and sandbox variants share volumes. - // Replace "myproject" with your project name (must match across both variants). + // Volume names end in ${localWorkspaceFolderBasename} so the default and sandbox + // variants share them. Replace "myproject" with your project name in both variants. "mounts": [ - // ── node_modules isolation (one per workspace) ───────────────────── - // Each workspace with a package.json needs its own volume mount. - // Without one, node_modules lands in the bind mount and shows up on the host. + // Every workspace with a package.json needs its own node_modules volume, or + // node_modules lands in the bind mount and shows up on the host. "source=myproject-node-modules-root-${localWorkspaceFolderBasename},target=/workspace/node_modules,type=volume", - // ── Monorepo: uncomment and customize for your structure ────────── // "source=myproject-node-modules-api-${localWorkspaceFolderBasename},target=/workspace/services/api/node_modules,type=volume", // "source=myproject-node-modules-web-${localWorkspaceFolderBasename},target=/workspace/apps/web/node_modules,type=volume", - // ── Persistent config ────────────────────────────────────────────── "source=myproject-claude-config-${localWorkspaceFolderBasename},target=/home/node/.claude,type=volume", "source=myproject-zsh-history-${localWorkspaceFolderBasename},target=/commandhistory,type=volume", - // gh CLI auth/state (~/.config/gh/hosts.yml) — keeps `gh auth login` across rebuilds "source=myproject-gh-config-${localWorkspaceFolderBasename},target=/home/node/.config/gh,type=volume" ], "containerEnv": { @@ -115,18 +89,13 @@ "NODE_OPTIONS": "--max-old-space-size=4096", "CLAUDE_CONFIG_DIR": "/home/node/.claude" }, - // sudo chown fixes volume ownership — safety net in case Docker volume population didn't apply. - // The find command chowns all node_modules volume mount points in one pass. - // mise install reads .mise.toml and installs project-specific tool versions. - // bun install is skipped until the project has a package.json. - // Chromium is baked into the image via apt — no playwright install step needed. - // Projects' playwright.config.ts should use process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH. + // The chowns are a safety net in case Docker didn't copy the image's ownership onto a + // new volume. Chromium comes from the image, so there is no playwright install step: + // point playwright.config.ts at process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH. "updateContentCommand": "sudo find /workspace -maxdepth 4 -name node_modules -type d -exec chown node {} + && sudo chown -R node /home/node/.claude /commandhistory /home/node/.config/gh && mise install && if [ -f package.json ]; then bun install; fi", - // Re-patch the Playwright MCP plugin's .mcp.json on every start. Defense in - // depth alongside the SessionStart hook in /etc/claude-code/managed-settings.json, - // which handles the case where the plugin auto-updates mid-container-run. - // See issues #85, #87, #98. // No postCreateCommand: claude CLI calls there race the extension's OAuth sign-in // (#58). Run `bash .devcontainer/init-plugins.sh` once after signing in. + // Re-patches Playwright MCP's .mcp.json on every start, as a backup to the SessionStart + // hook in /etc/claude-code/managed-settings.json (#85, #87, #98). "postStartCommand": "/usr/local/bin/patch-playwright-mcp" } diff --git a/claude-code/.devcontainer/init-plugins.sh b/claude-code/.devcontainer/init-plugins.sh index ebcf56c..b8576cb 100755 --- a/claude-code/.devcontainer/init-plugins.sh +++ b/claude-code/.devcontainer/init-plugins.sh @@ -101,7 +101,6 @@ if command -v rtk &>/dev/null; then fi # ── agent-browser skill ───────────────────────────────────────────────────── -# Installs the agent-browser Claude Code skill for headless browser automation. # agent-browser CLI is pre-installed in the default devcontainer image (not sandbox). if command -v agent-browser &>/dev/null; then agent-browser install-skill 2>/dev/null || true diff --git a/hugo-bun-node/.devcontainer/devcontainer.json b/hugo-bun-node/.devcontainer/devcontainer.json index dae2d43..ae347d6 100644 --- a/hugo-bun-node/.devcontainer/devcontainer.json +++ b/hugo-bun-node/.devcontainer/devcontainer.json @@ -11,17 +11,12 @@ "source=${localWorkspaceFolderBasename}-node_modules,target=${containerWorkspaceFolder}/node_modules,type=volume" ], - // Configure tool-specific properties. "customizations": { - // Configure properties specific to VS Code. "vscode": { - // Use zsh as the default shell for better VS Code integration "settings": { "terminal.integrated.defaultProfile.linux": "zsh", - // Suppress extension recommendation prompts (e.g., Container Tools in Codespaces) "extensions.ignoreRecommendations": true }, - // Add the IDs of extensions you want installed when the container is created. "extensions": [ // **Claude Code** "anthropic.claude-code", @@ -29,21 +24,15 @@ // **Bun** "oven.bun-vscode", - // Prettier - Code Formatter "esbenp.prettier-vscode", // **Tailwind** - // Tailwind CSS IntelliSense "bradlc.vscode-tailwindcss", - // Tailwind Fold "stivo.tailwind-fold", // **Hugo** - // Hugofy - Hugo-related extension "akmittal.hugofy", - // Language Hugo VSCode - Hugo language support "budparr.language-hugo-vscode", - // Hugo Shortcode Syntax - Hugo shortcode syntax "kaellarkin.hugo-shortcode-syntax" ] } diff --git a/hugo-bun/.devcontainer/devcontainer.json b/hugo-bun/.devcontainer/devcontainer.json index dae2d43..ae347d6 100644 --- a/hugo-bun/.devcontainer/devcontainer.json +++ b/hugo-bun/.devcontainer/devcontainer.json @@ -11,17 +11,12 @@ "source=${localWorkspaceFolderBasename}-node_modules,target=${containerWorkspaceFolder}/node_modules,type=volume" ], - // Configure tool-specific properties. "customizations": { - // Configure properties specific to VS Code. "vscode": { - // Use zsh as the default shell for better VS Code integration "settings": { "terminal.integrated.defaultProfile.linux": "zsh", - // Suppress extension recommendation prompts (e.g., Container Tools in Codespaces) "extensions.ignoreRecommendations": true }, - // Add the IDs of extensions you want installed when the container is created. "extensions": [ // **Claude Code** "anthropic.claude-code", @@ -29,21 +24,15 @@ // **Bun** "oven.bun-vscode", - // Prettier - Code Formatter "esbenp.prettier-vscode", // **Tailwind** - // Tailwind CSS IntelliSense "bradlc.vscode-tailwindcss", - // Tailwind Fold "stivo.tailwind-fold", // **Hugo** - // Hugofy - Hugo-related extension "akmittal.hugofy", - // Language Hugo VSCode - Hugo language support "budparr.language-hugo-vscode", - // Hugo Shortcode Syntax - Hugo shortcode syntax "kaellarkin.hugo-shortcode-syntax" ] } From 955be2713fcd651f1387482cf92d1e9b84d7487c Mon Sep 17 00:00:00 2001 From: Serge Gatezh <2880401+gatezh@users.noreply.github.com> Date: Tue, 6 Oct 2026 13:49:07 -0600 Subject: [PATCH 2/2] chore: keep extension descriptions and other useful devcontainer comments Narrow the cleanup to comments that only repeat the line below them. Extension descriptions, the formatter hint, section headers and the explanatory comments return as they were on master. --- .claude/rules/devcontainer.md | 5 +- .../claude-sandbox/devcontainer.json | 5 ++ .devcontainer/claude-sandbox/init-firewall.sh | 3 + .devcontainer/devcontainer.json | 10 +++ bun/.devcontainer/devcontainer.json | 6 ++ claude-bun/.devcontainer/devcontainer.json | 9 ++- .../claude-sandbox/devcontainer.json | 62 ++++++++++++++----- claude-code/.devcontainer/devcontainer.json | 59 +++++++++++++----- claude-code/.devcontainer/init-plugins.sh | 1 + hugo-bun-node/.devcontainer/devcontainer.json | 9 +++ hugo-bun/.devcontainer/devcontainer.json | 9 +++ 11 files changed, 145 insertions(+), 33 deletions(-) diff --git a/.claude/rules/devcontainer.md b/.claude/rules/devcontainer.md index 6e81f4e..726e9bd 100644 --- a/.claude/rules/devcontainer.md +++ b/.claude/rules/devcontainer.md @@ -14,7 +14,7 @@ paths: ## Comments -Comment only what the JSON can't say: a reason, a constraint, an issue number, or what a template adopter must change. A comment that restates the key below it (`// Show workspace folder name in window title` above `"window.title"`) is noise; leave it out. +Skip a comment that only repeats the line below it in other words, such as `// Show workspace folder name in window title` above `"window.title"`. Extension descriptions are not that: they say what an extension ID does. ## node_modules Mount @@ -28,11 +28,12 @@ Always include to keep node_modules off the host: ## VS Code Extensions -Group by category with a header comment. No per-extension description: the ID names the extension. +Group by category with header comments: ```jsonc "extensions": [ // **Category Name** + // Extension Description "publisher.extension-id" ] ``` diff --git a/.devcontainer/claude-sandbox/devcontainer.json b/.devcontainer/claude-sandbox/devcontainer.json index 3fcffac..5bf037f 100644 --- a/.devcontainer/claude-sandbox/devcontainer.json +++ b/.devcontainer/claude-sandbox/devcontainer.json @@ -33,6 +33,10 @@ "bash": { "path": "bash", "icon": "terminal-bash" } }, "extensions.ignoreRecommendations": true, + // ── Formatter settings (customize per project) ────────────────── + // Change "editor.defaultFormatter" to match your tooling: + // Biome: "biomejs.biome" | Prettier: "esbenp.prettier-vscode" + // OXC: "oxc.oxc-vscode" | None: remove these three settings "editor.formatOnSave": true, "editor.defaultFormatter": "oxc.oxc-vscode", "editor.codeActionsOnSave": { @@ -40,6 +44,7 @@ "source.organizeImports": "explicit" }, "window.title": "${localWorkspaceFolderBasename}", + // Sandbox visual identity — Claude Dark theme with coral remote indicator "workbench.colorTheme": "Claude Dark", "workbench.colorCustomizations": { "statusBarItem.remoteBackground": "#C15F3C", diff --git a/.devcontainer/claude-sandbox/init-firewall.sh b/.devcontainer/claude-sandbox/init-firewall.sh index 44f1c15..6f98f4d 100755 --- a/.devcontainer/claude-sandbox/init-firewall.sh +++ b/.devcontainer/claude-sandbox/init-firewall.sh @@ -5,6 +5,7 @@ IFS=$'\n\t' # 1. Extract Docker DNS info BEFORE any flushing DOCKER_DNS_RULES=$(iptables-save -t nat | grep "127\.0\.0\.11" || true) +# Flush existing rules and delete existing ipsets iptables -F iptables -X iptables -t nat -F @@ -31,8 +32,10 @@ iptables -A INPUT -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT iptables -A INPUT -i lo -j ACCEPT iptables -A OUTPUT -o lo -j ACCEPT +# Create ipset with CIDR support ipset create allowed-domains hash:net +# Fetch GitHub IP ranges echo "Fetching GitHub IP ranges..." gh_ranges=$(curl -s https://api.github.com/meta) if [ -z "$gh_ranges" ]; then diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 0c2c64f..439d7fc 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -15,12 +15,17 @@ "vscode": { "extensions": [ // **AI Agents** + // Claude Code - AI assistant "anthropic.claude-code", // **General** + // YAML - YAML support (for workflows and docker-compose) "redhat.vscode-yaml", + // Markdown Preview Github Styles - Markdown preview "bierner.markdown-preview-github-styles", + // Markdown Preview Mermaid Support - Mermaid diagrams "bierner.markdown-mermaid", + // Docker - Dockerfile support "ms-azuretools.vscode-docker" ], "settings": { @@ -30,6 +35,10 @@ "bash": { "path": "bash", "icon": "terminal-bash" } }, "extensions.ignoreRecommendations": true, + // ── Formatter settings (customize per project) ────────────────── + // Change "editor.defaultFormatter" to match your tooling: + // Biome: "biomejs.biome" | Prettier: "esbenp.prettier-vscode" + // OXC: "oxc.oxc-vscode" | None: remove these three settings "editor.formatOnSave": true, "editor.defaultFormatter": "oxc.oxc-vscode", "editor.codeActionsOnSave": { @@ -37,6 +46,7 @@ "source.organizeImports": "explicit" }, "window.title": "${localWorkspaceFolderBasename}", + // Visual identity — Claude Dark theme with coral remote indicator "workbench.colorTheme": "Claude Dark", "workbench.colorCustomizations": { "statusBarItem.remoteBackground": "#C15F3C", diff --git a/bun/.devcontainer/devcontainer.json b/bun/.devcontainer/devcontainer.json index 88cbe9b..05e4e47 100644 --- a/bun/.devcontainer/devcontainer.json +++ b/bun/.devcontainer/devcontainer.json @@ -13,10 +13,13 @@ "customizations": { "vscode": { + // Use zsh as the default shell for better VS Code integration "settings": { "terminal.integrated.defaultProfile.linux": "zsh", + // Suppress extension recommendation prompts (e.g., Container Tools in Codespaces) "extensions.ignoreRecommendations": true }, + // Add the IDs of extensions you want installed when the container is created. "extensions": [ // **Claude Code** "anthropic.claude-code", @@ -24,10 +27,13 @@ // **Bun** "oven.bun-vscode", + // Prettier - Code Formatter "esbenp.prettier-vscode", // **Tailwind** + // Tailwind CSS IntelliSense "bradlc.vscode-tailwindcss", + // Tailwind Fold "stivo.tailwind-fold" ] } diff --git a/claude-bun/.devcontainer/devcontainer.json b/claude-bun/.devcontainer/devcontainer.json index 6a27376..804d1b0 100644 --- a/claude-bun/.devcontainer/devcontainer.json +++ b/claude-bun/.devcontainer/devcontainer.json @@ -18,13 +18,15 @@ "mounts": [ // Keep node_modules out of a host machine "source=${localWorkspaceFolderBasename}-node_modules,target=${containerWorkspaceFolder}/node_modules,type=volume", - // zsh writes its history here (HISTFILE in the Dockerfile); the volume name is legacy. + // Persist shell history between container rebuilds. zsh writes here + // (HISTFILE in the Dockerfile); the volume name is legacy. "source=claude-code-bashhistory-${devcontainerId},target=/commandhistory,type=volume", "source=claude-code-config-${devcontainerId},target=/home/bun/.claude,type=volume" ], "customizations": { "vscode": { + // Use zsh as the default shell for better VS Code integration "settings": { "terminal.integrated.defaultProfile.linux": "zsh", "terminal.integrated.profiles.linux": { @@ -36,6 +38,7 @@ "path": "zsh" } }, + // Suppress extension recommendation prompts (e.g., Container Tools in Codespaces) "extensions.ignoreRecommendations": true, "editor.formatOnSave": true, "editor.defaultFormatter": "biomejs.biome", @@ -44,6 +47,7 @@ "source.organizeImports.biome": "explicit" } }, + // Add the IDs of extensions you want installed when the container is created. "extensions": [ // **Claude Code** "anthropic.claude-code", @@ -52,10 +56,13 @@ "oven.bun-vscode", // **Code Quality** + // Biome - Fast formatter and linter for JavaScript/TypeScript "biomejs.biome", // **Tailwind** + // Tailwind CSS IntelliSense "bradlc.vscode-tailwindcss", + // Tailwind Fold "stivo.tailwind-fold" ] } diff --git a/claude-code/.devcontainer/claude-sandbox/devcontainer.json b/claude-code/.devcontainer/claude-sandbox/devcontainer.json index b00c2cd..2db7413 100644 --- a/claude-code/.devcontainer/claude-sandbox/devcontainer.json +++ b/claude-code/.devcontainer/claude-sandbox/devcontainer.json @@ -4,9 +4,13 @@ "dockerComposeFile": "docker-compose.yml", "service": "devcontainer", "workspaceFolder": "/workspace", - // initializeCommand: see the default variant. + // Pull the prebuilt image on the HOST before the build, so "Rebuild Without + // Cache" always layers on the freshest base. Replaces `pull_policy: always` in + // docker-compose.yml, which breaks on Linux/WSL2 when `updateRemoteUserUID` + // builds a local-only image (issue #109). `|| exit 0` keeps offline/outage opens + // working (valid in POSIX sh and Windows cmd.exe). Keep in sync with the compose image. "initializeCommand": "docker pull ghcr.io/gatezh/devcontainers/claude-code-sandbox:latest || exit 0", - // For the iptables firewall. + // Capabilities required for iptables firewall setup "capAdd": ["NET_ADMIN", "NET_RAW"], "init": true, "updateRemoteUserUID": true, @@ -16,42 +20,59 @@ "vscode": { "extensions": [ // **AI Agents** + // Claude Code - AI coding assistant with chat, inline edits, and terminal integration "anthropic.claude-code", // **Runtime** + // Bun - Bun runtime support (debugging, lockfile viewer, bundler integration) "oven.bun-vscode", // **Code Quality** + // OXC - Fast linter and formatter for JavaScript/TypeScript (Rust-based) "oxc.oxc-vscode", // **Tailwind** + // Tailwind CSS IntelliSense - autocomplete, syntax highlighting, linting for Tailwind classes "bradlc.vscode-tailwindcss", + // Tailwind Fold - collapse long Tailwind class strings in the editor for readability "stivo.tailwind-fold", // **General** + // YAML - YAML language support (for workflows and docker-compose) "redhat.vscode-yaml", + // Markdown Preview Github Styles - renders Markdown preview with GitHub's CSS "bierner.markdown-preview-github-styles", + // Markdown Preview Mermaid Support - renders Mermaid diagrams in Markdown preview "bierner.markdown-mermaid", + // Docker - Dockerfile and Compose syntax, linting, and image management "ms-azuretools.vscode-docker", // **Spelling** + // Code Spell Checker - catches typos in code and comments "streetsidesoftware.code-spell-checker", // **JavaScript / TypeScript** + // npm IntelliSense - autocomplete npm imports in import statements "christian-kohler.npm-intellisense", + // TypeScript Error Translator - human-readable TS error messages "mattpocock.ts-error-translator", // **CSS / Colors** + // Color Info - preview CSS colors on hover "bierner.color-info", + // Colorize - visualize CSS colors inline in the editor "kamikillerto.vscode-colorize", // **Frameworks** + // Drizzle ORM Snippets - code snippets for Drizzle ORM "drizzle-team.drizzle-orm-snippets", // **Testing** + // Playwright - run, debug, and generate Playwright tests from VS Code "ms-playwright.playwright", // **Sandbox Theme** + // Claude Theme - provides "Claude Dark" referenced by workbench.colorTheme "SamiHindi.claude-theme-sami-hindi" ], "settings": { @@ -68,7 +89,7 @@ "source.organizeImports": "explicit" }, "window.title": "${localWorkspaceFolderBasename}", - // Tells a sandbox window apart from the default variant at a glance. + // Sandbox visual identity — Claude Dark theme with coral remote indicator "workbench.colorTheme": "Claude Dark", "workbench.colorCustomizations": { "statusBarItem.remoteBackground": "#C15F3C", @@ -78,19 +99,25 @@ } } }, - // Volume names end in ${localWorkspaceFolderBasename} so the default and sandbox - // variants share them. Replace "myproject" with your project name in both variants. + // Named volumes keep node_modules OFF the host machine and persist across rebuilds. + // Uses ${localWorkspaceFolderBasename} so default and sandbox variants share volumes. + // Replace "myproject" with your project name (must match across both variants). "mounts": [ - // Every workspace with a package.json needs its own node_modules volume, or - // node_modules lands in the bind mount and shows up on the host. + // ── node_modules isolation (one per workspace) ───────────────────── + // Each workspace with a package.json needs its own volume mount. + // Without one, node_modules lands in the bind mount and shows up on the host. "source=myproject-node-modules-root-${localWorkspaceFolderBasename},target=/workspace/node_modules,type=volume", + // ── Monorepo: uncomment and customize for your structure ────────── // "source=myproject-node-modules-api-${localWorkspaceFolderBasename},target=/workspace/services/api/node_modules,type=volume", // "source=myproject-node-modules-web-${localWorkspaceFolderBasename},target=/workspace/apps/web/node_modules,type=volume", + // ── Persistent config ────────────────────────────────────────────── "source=myproject-claude-config-${localWorkspaceFolderBasename},target=/home/node/.claude,type=volume", "source=myproject-zsh-history-${localWorkspaceFolderBasename},target=/commandhistory,type=volume", + // gh CLI auth/state (~/.config/gh/hosts.yml) — keeps `gh auth login` across rebuilds "source=myproject-gh-config-${localWorkspaceFolderBasename},target=/home/node/.config/gh,type=volume", - // The image has iptables, ipset and the sudo rule but not the script: each project - // mounts its own, with its own domain allowlist. + // ── Firewall script ──────────────────────────────────────────────── + // The image provides iptables/ipset packages and sudo rule but NOT the script itself. + // Each project provides its own script via bind mount to customize the domain allowlist. "source=${localWorkspaceFolder}/.devcontainer/claude-sandbox/init-firewall.sh,target=/usr/local/bin/init-firewall.sh,type=bind" ], "containerEnv": { @@ -98,15 +125,20 @@ "DEVCONTAINER": "true", "NODE_OPTIONS": "--max-old-space-size=4096", "CLAUDE_CONFIG_DIR": "/home/node/.claude", - // Only needed without the default variant, whose sign-in lands on the shared - // ~/.claude volume. See "Sandbox Authentication" in the README. + // Optional — only needed when the sandbox is used without the default variant, whose + // sign-in lands on the shared ~/.claude volume. Resolves to "" when the host var is + // unset. See "Sandbox Authentication" section in README. "CLAUDE_CODE_OAUTH_TOKEN": "${localEnv:CLAUDE_CODE_OAUTH_TOKEN}" }, - // Plugins install here, before postStartCommand brings the firewall up (which is also - // why Chromium comes from the image). The chowns are the same safety net as in the - // default variant. + // The find command chowns all node_modules volume mount points in one pass. + // Chromium is baked into the sandbox image (firewall blocks runtime install). + // bun install and init-plugins.sh are skipped when their file is absent. Plugins + // install here, before postStartCommand brings the firewall up. "postCreateCommand": "sudo find /workspace -maxdepth 4 -name node_modules -type d -exec chown node {} + && sudo chown -R node /home/node/.claude /commandhistory /home/node/.config/gh && mise install && if [ -f package.json ]; then bun install; fi && if [ -f .devcontainer/init-plugins.sh ]; then bash .devcontainer/init-plugins.sh; fi", - // patch-playwright-mcp: see the default variant. + // Firewall init (bind-mounted from project) + re-patch the Playwright MCP + // plugin's .mcp.json. The patch is defense in depth alongside the SessionStart + // hook in /etc/claude-code/managed-settings.json, which handles the case where + // the plugin auto-updates mid-container-run. See issues #85, #87, #98. "postStartCommand": "sudo /usr/local/bin/init-firewall.sh && /usr/local/bin/patch-playwright-mcp", "waitFor": "postStartCommand" } diff --git a/claude-code/.devcontainer/devcontainer.json b/claude-code/.devcontainer/devcontainer.json index a4e6f0b..37fb196 100644 --- a/claude-code/.devcontainer/devcontainer.json +++ b/claude-code/.devcontainer/devcontainer.json @@ -4,10 +4,11 @@ "dockerComposeFile": "docker-compose.yml", "service": "devcontainer", "workspaceFolder": "/workspace", - // Pulls on the HOST so "Rebuild Without Cache" layers on the freshest image. Compose's - // `pull_policy: always` breaks on Linux/WSL2, where `updateRemoteUserUID` builds a - // local-only image (#109). `|| exit 0` (valid in sh and cmd.exe) keeps offline opens - // working. Keep the image in sync with docker-compose.yml. + // Pull the prebuilt image on the HOST before the build, so "Rebuild Without + // Cache" always layers on the freshest base. Replaces `pull_policy: always` in + // docker-compose.yml, which breaks on Linux/WSL2 when `updateRemoteUserUID` + // builds a local-only image (issue #109). `|| exit 0` keeps offline/outage opens + // working (valid in POSIX sh and Windows cmd.exe). Keep in sync with the compose image. "initializeCommand": "docker pull ghcr.io/gatezh/devcontainers/claude-code:latest || exit 0", "init": true, "updateRemoteUserUID": true, @@ -17,39 +18,55 @@ "vscode": { "extensions": [ // **AI Agents** + // Claude Code - AI coding assistant with chat, inline edits, and terminal integration "anthropic.claude-code", // **Runtime** + // Bun - Bun runtime support (debugging, lockfile viewer, bundler integration) "oven.bun-vscode", // **Code Quality** + // OXC - Fast linter and formatter for JavaScript/TypeScript (Rust-based) "oxc.oxc-vscode", // **Tailwind** + // Tailwind CSS IntelliSense - autocomplete, syntax highlighting, linting for Tailwind classes "bradlc.vscode-tailwindcss", + // Tailwind Fold - collapse long Tailwind class strings in the editor for readability "stivo.tailwind-fold", // **General** + // YAML - YAML language support (for workflows and docker-compose) "redhat.vscode-yaml", + // Markdown Preview Github Styles - renders Markdown preview with GitHub's CSS "bierner.markdown-preview-github-styles", + // Markdown Preview Mermaid Support - renders Mermaid diagrams in Markdown preview "bierner.markdown-mermaid", + // Docker - Dockerfile and Compose syntax, linting, and image management "ms-azuretools.vscode-docker", // **Spelling** + // Code Spell Checker - catches typos in code and comments "streetsidesoftware.code-spell-checker", // **JavaScript / TypeScript** + // npm IntelliSense - autocomplete npm imports in import statements "christian-kohler.npm-intellisense", + // TypeScript Error Translator - human-readable TS error messages "mattpocock.ts-error-translator", // **CSS / Colors** + // Color Info - preview CSS colors on hover "bierner.color-info", + // Colorize - visualize CSS colors inline in the editor "kamikillerto.vscode-colorize", // **Frameworks** + // Drizzle ORM Snippets - code snippets for Drizzle ORM "drizzle-team.drizzle-orm-snippets", // **Testing** + // Playwright - run, debug, and generate Playwright tests from VS Code "ms-playwright.playwright" ], "settings": { @@ -59,8 +76,10 @@ "bash": { "path": "bash", "icon": "terminal-bash" } }, "extensions.ignoreRecommendations": true, - // Set the formatter to your project's tool (Biome "biomejs.biome", Prettier - // "esbenp.prettier-vscode"), or remove these three settings. + // ── Formatter settings (customize per project) ────────────────── + // Change "editor.defaultFormatter" to match your tooling: + // Biome: "biomejs.biome" | Prettier: "esbenp.prettier-vscode" + // OXC: "oxc.oxc-vscode" | None: remove these three settings "editor.formatOnSave": true, "editor.defaultFormatter": "oxc.oxc-vscode", "editor.codeActionsOnSave": { @@ -71,16 +90,21 @@ } } }, - // Volume names end in ${localWorkspaceFolderBasename} so the default and sandbox - // variants share them. Replace "myproject" with your project name in both variants. + // Named volumes keep node_modules OFF the host machine and persist across rebuilds. + // Uses ${localWorkspaceFolderBasename} so default and sandbox variants share volumes. + // Replace "myproject" with your project name (must match across both variants). "mounts": [ - // Every workspace with a package.json needs its own node_modules volume, or - // node_modules lands in the bind mount and shows up on the host. + // ── node_modules isolation (one per workspace) ───────────────────── + // Each workspace with a package.json needs its own volume mount. + // Without one, node_modules lands in the bind mount and shows up on the host. "source=myproject-node-modules-root-${localWorkspaceFolderBasename},target=/workspace/node_modules,type=volume", + // ── Monorepo: uncomment and customize for your structure ────────── // "source=myproject-node-modules-api-${localWorkspaceFolderBasename},target=/workspace/services/api/node_modules,type=volume", // "source=myproject-node-modules-web-${localWorkspaceFolderBasename},target=/workspace/apps/web/node_modules,type=volume", + // ── Persistent config ────────────────────────────────────────────── "source=myproject-claude-config-${localWorkspaceFolderBasename},target=/home/node/.claude,type=volume", "source=myproject-zsh-history-${localWorkspaceFolderBasename},target=/commandhistory,type=volume", + // gh CLI auth/state (~/.config/gh/hosts.yml) — keeps `gh auth login` across rebuilds "source=myproject-gh-config-${localWorkspaceFolderBasename},target=/home/node/.config/gh,type=volume" ], "containerEnv": { @@ -89,13 +113,18 @@ "NODE_OPTIONS": "--max-old-space-size=4096", "CLAUDE_CONFIG_DIR": "/home/node/.claude" }, - // The chowns are a safety net in case Docker didn't copy the image's ownership onto a - // new volume. Chromium comes from the image, so there is no playwright install step: - // point playwright.config.ts at process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH. + // sudo chown fixes volume ownership — safety net in case Docker volume population didn't apply. + // The find command chowns all node_modules volume mount points in one pass. + // mise install reads .mise.toml and installs project-specific tool versions. + // bun install is skipped until the project has a package.json. + // Chromium is baked into the image via apt — no playwright install step needed. + // Projects' playwright.config.ts should use process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH. "updateContentCommand": "sudo find /workspace -maxdepth 4 -name node_modules -type d -exec chown node {} + && sudo chown -R node /home/node/.claude /commandhistory /home/node/.config/gh && mise install && if [ -f package.json ]; then bun install; fi", + // Re-patch the Playwright MCP plugin's .mcp.json on every start. Defense in + // depth alongside the SessionStart hook in /etc/claude-code/managed-settings.json, + // which handles the case where the plugin auto-updates mid-container-run. + // See issues #85, #87, #98. // No postCreateCommand: claude CLI calls there race the extension's OAuth sign-in // (#58). Run `bash .devcontainer/init-plugins.sh` once after signing in. - // Re-patches Playwright MCP's .mcp.json on every start, as a backup to the SessionStart - // hook in /etc/claude-code/managed-settings.json (#85, #87, #98). "postStartCommand": "/usr/local/bin/patch-playwright-mcp" } diff --git a/claude-code/.devcontainer/init-plugins.sh b/claude-code/.devcontainer/init-plugins.sh index b8576cb..ebcf56c 100755 --- a/claude-code/.devcontainer/init-plugins.sh +++ b/claude-code/.devcontainer/init-plugins.sh @@ -101,6 +101,7 @@ if command -v rtk &>/dev/null; then fi # ── agent-browser skill ───────────────────────────────────────────────────── +# Installs the agent-browser Claude Code skill for headless browser automation. # agent-browser CLI is pre-installed in the default devcontainer image (not sandbox). if command -v agent-browser &>/dev/null; then agent-browser install-skill 2>/dev/null || true diff --git a/hugo-bun-node/.devcontainer/devcontainer.json b/hugo-bun-node/.devcontainer/devcontainer.json index ae347d6..61355d6 100644 --- a/hugo-bun-node/.devcontainer/devcontainer.json +++ b/hugo-bun-node/.devcontainer/devcontainer.json @@ -13,10 +13,13 @@ "customizations": { "vscode": { + // Use zsh as the default shell for better VS Code integration "settings": { "terminal.integrated.defaultProfile.linux": "zsh", + // Suppress extension recommendation prompts (e.g., Container Tools in Codespaces) "extensions.ignoreRecommendations": true }, + // Add the IDs of extensions you want installed when the container is created. "extensions": [ // **Claude Code** "anthropic.claude-code", @@ -24,15 +27,21 @@ // **Bun** "oven.bun-vscode", + // Prettier - Code Formatter "esbenp.prettier-vscode", // **Tailwind** + // Tailwind CSS IntelliSense "bradlc.vscode-tailwindcss", + // Tailwind Fold "stivo.tailwind-fold", // **Hugo** + // Hugofy - Hugo-related extension "akmittal.hugofy", + // Language Hugo VSCode - Hugo language support "budparr.language-hugo-vscode", + // Hugo Shortcode Syntax - Hugo shortcode syntax "kaellarkin.hugo-shortcode-syntax" ] } diff --git a/hugo-bun/.devcontainer/devcontainer.json b/hugo-bun/.devcontainer/devcontainer.json index ae347d6..61355d6 100644 --- a/hugo-bun/.devcontainer/devcontainer.json +++ b/hugo-bun/.devcontainer/devcontainer.json @@ -13,10 +13,13 @@ "customizations": { "vscode": { + // Use zsh as the default shell for better VS Code integration "settings": { "terminal.integrated.defaultProfile.linux": "zsh", + // Suppress extension recommendation prompts (e.g., Container Tools in Codespaces) "extensions.ignoreRecommendations": true }, + // Add the IDs of extensions you want installed when the container is created. "extensions": [ // **Claude Code** "anthropic.claude-code", @@ -24,15 +27,21 @@ // **Bun** "oven.bun-vscode", + // Prettier - Code Formatter "esbenp.prettier-vscode", // **Tailwind** + // Tailwind CSS IntelliSense "bradlc.vscode-tailwindcss", + // Tailwind Fold "stivo.tailwind-fold", // **Hugo** + // Hugofy - Hugo-related extension "akmittal.hugofy", + // Language Hugo VSCode - Hugo language support "budparr.language-hugo-vscode", + // Hugo Shortcode Syntax - Hugo shortcode syntax "kaellarkin.hugo-shortcode-syntax" ] }