From aicontainer
Configures aicontainer sandboxed devcontainer for AI coding agents in bypass/auto-approve mode. Detects project stack, proposes customization, and applies setup.
How this skill is triggered — by the user, by Claude, or both
Slash command
/aicontainer:aicontainer-setupThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Your job is to take a project from "no sandbox" to "the AI agent runs behind the
Your job is to take a project from "no sandbox" to "the AI agent runs behind the aicontainer devcontainer boundary, configured for this project's stack." You detect the stack, sanity-check that a Linux devcontainer is even the right tool, propose a concrete customization plan, and — once the user approves — apply it.
aicontainer's whole point is letting Claude Code / Codex / OpenCode run with
permissions skipped without handing a prompt-injected dependency your real
$HOME, .env, SSH keys, or gh token. Good setup is what makes that sandbox
actually usable for the project — a Python repo needs a writable .venv, an
agent that uses go-to-definition needs a language server on PATH, a Postgres
app needs the DB reachable. That per-project wiring is the work here.
These come straight from how aicontainer is designed; violating them creates work
that silently gets wiped or breaks aic sync.
aic from the host, never from inside the container. Setup happens
before (or outside) the sandbox. If you detect you're inside an aicontainer
already (see Step 0), stop and tell the user to run this from a host terminal —
.devcontainer/ is mounted read-only in the sandbox by design..devcontainer/devcontainer.json or
.devcontainer/docker-compose.yml. They are template-managed: aic init
writes them and aic sync overwrites them (only the AIC_TOOLS / AIC_SHELL
choices survive). All per-project customization goes in the project-owned
files instead (listed in Step 5). Edits to the managed files get reset on the
next sync.aic sync never
touches: Dockerfile.project, docker-compose.override.yml, chown-paths,
post-create.project.sh, vscode-extensions, vscode-settings.json,
firewall-allowlist. These are opt-in by presence.aic init / aic sync. Stop before
aic up — it pulls a multi-gigabyte image; hand that command to the user.Before anything, confirm the ground is solid:
/etc/aic exists,
or env vars like REMOTE_CONTAINERS / DEVCONTAINER / AIC_TOOLS are set,
you're inside the sandbox — .devcontainer/ writes are blocked by the
PreToolUse hook. Stop and tell the user to run setup from a host terminal.aic init expects a project directory. If there's no
repo, that's fine, but note it (the sandbox bind-mounts $PWD as /workspace
regardless).aic CLI installed? Check command -v aic. If it's missing,
don't abort — offer to install it, since nothing else in this skill works
without it:
node --version) — it's the one
prerequisite for the npm install (and the bundled @devcontainers/cli). If
Node is too old or absent, say so and point the user at it rather than
attempting the install.npm install -g aicontainer. If the user prefers a git checkout, the
alternative is
git clone https://github.com/stefanoginella/aicontainer ~/.aicontainer && ~/.aicontainer/install.sh
(a checkout also needs npm install -g @devcontainers/cli separately).command -v aic (or aic --version)
and confirm it's now on PATH before continuing. If the install failed or
aic still isn't found, stop and surface the error; don't push ahead into
the steps below.docker info should succeed (Docker Desktop / OrbStack
/ Colima). Setup can still proceed without it, but aic up later will need it
— note it rather than blocking..devcontainer/devcontainer.json
exists and references aicontainer (the GHCR image or AIC_TOOLS), this is a
re-setup, not a fresh install — you're in update mode. Don't regenerate
from scratch and don't re-propose files that already exist and are correct.
Still read the README (Step 1) and re-detect/confirm the stack (Steps 2–3),
then follow "Update mode" below instead of the greenfield Step 5. (If no
.devcontainer/ exists, it's a fresh setup — continue straight through
Steps 1–8.)When Step 0 found an existing aic setup, the job is not "generate config." It
is "check whether this project's personalization still fits its stack, and update
only what's missing, stale, or drifted." A project set up months ago may have
gained a language, a service, browser e2e tests, or new build artifacts — or had
the global aic upgraded under it. Read the README (Step 1) and refresh the
stack profile (Steps 2–3) first, then:
Dockerfile.project, docker-compose.override.yml,
chown-paths, post-create.project.sh, vscode-extensions,
vscode-settings.json, firewall-allowlist. Also note the current
AIC_TOOLS / AIC_SHELL in devcontainer.json (the only managed values that
survive aic sync). This is your baseline — what's already covered.post-create.project.sh and no editor
extension; a Python project with no writable .venv volume; a newly-added
Postgres/Redis service not wired in docker-compose.override.yml;
Playwright/Chromium added with no Dockerfile.project.chown-paths line for a volume that's no longer declared; a
vscode-settings.json interpreter path that no longer exists; an LSP install
or extension for a language the project dropped; a tool in AIC_TOOLS the
user no longer wants.npm update -g aicontainer, a Dockerfile.project whose FROM …:vX.Y.Z
lags the tag now pinned in docker-compose.yml. aic sync warns; the
fix is aic sync --bump-base (it rewrites the FROM in place — never
hand-edit a project-owned file's base for this).aic sync (the re-setup verb) so override wiring and the
vscode-extensions / vscode-settings.json merges refresh. Stop before
aic up / aic rebuild.Fetch the live README so your recipes match the current release (override-file
syntax and the Dockerfile.project / LSP recipes occasionally change):
https://raw.githubusercontent.com/stefanoginella/aicontainer/main/README.mdhttps://github.com/stefanoginella/aicontainerIf the fetch fails (offline, firewall), degrade gracefully — don't abort. Use, in
order: (a) the .devcontainer/README.md that aic init drops into the project
(version-matched, documents the managed-vs-project-owned split), (b) aic help,
and (c) references/stack-and-suitability.md in this skill. The README is the
source of truth for recipe syntax; this skill carries the judgment (stack
detection, suitability, LSP-by-language).
Build a picture of what this project actually is. Read, don't guess. Cover:
package.json, pyproject.toml / requirements.txt
/ uv.lock, go.mod, Cargo.toml, Gemfile, pom.xml / build.gradle,
composer.json, *.csproj / *.sln, mix.exs, pubspec.yaml,
Package.swift, *.xcodeproj, CMakeLists.txt..nvmrc, .python-version, .tool-versions,
.ruby-version, Dockerfile, existing docker-compose.yml.docker-compose.yml services, .env.example
/ .env.sample keys (DATABASE_URL, REDIS_URL, …), ORM/migration config.
These become host-service or sibling-container wiring.@playwright/test,
cypress, puppeteer) and native build steps, which need a Dockerfile.project..md design docs — README.md, ARCHITECTURE.md, docs/, PRD / spec /
design files. Lean on these when code signals are thin or the repo is greenfield;
a spec saying "React + FastAPI + Postgres" is a real signal.references/stack-and-suitability.md has the full signal→inference table. The
goal is a short profile: language(s) + package manager(s) + runtime versions +
services + test tooling + any GUI/mobile/native/hardware signals.
Don't proceed on a guess. Present the detected profile compactly and ask the user to confirm or correct it — use AskUserQuestion when the choice is crisp, plain prose when it's open. If detection was inconclusive (sparse repo, unfamiliar layout), ask the user directly what the stack is rather than inventing one. The customization plan is only as good as this profile, so it's worth the round-trip.
aicontainer is a headless Linux devcontainer. Most server / web / CLI /
library / data / ML-on-CPU work is a great fit. Some work fundamentally can't run
here — flag it before generating config so the user isn't surprised after a
rebuild. Use the matrix in references/stack-and-suitability.md. The headline
non-fits, and why:
nvidia-container-toolkit); possible
but advanced and may not be supported.When you hit a non-fit, say so plainly, explain what specifically won't work, and note whether a partial setup still helps (e.g. an iOS app with a Node backend — sandbox the backend, build the app on the host). Then let the user decide whether to proceed. Don't silently generate a config that can't work.
First the two aic init choices (ask, or take the defaults and say so):
--with): claude-code, codex, opencode, or a comma-list.
Default is all three.--shell): zsh (default), bash, or fish.Then map the confirmed stack to project-owned files. The aim is a sandbox the
project actually builds and runs in. Common mappings (full syntax: the README from
Step 1; rationale and the LSP-by-language table: references/stack-and-suitability.md):
chown-paths — for build artifacts you don't want on the
bind-mounted workspace: Python .venv + uv cache, Node node_modules, Rust
target/cargo, Go build cache. Persists across rebuilds and dodges the macOS
bind-mount perf hit. Always pair a named volume with a chown-paths entry —
Docker inits named volumes root:root and the mount is otherwise unwritable by
vscode. Keep caches under /workspace/ or /home/vscode/.cache/ (the only
paths chown-paths honors).Dockerfile.project — for anything needing apt / root or baked-in
browsers: native build deps, DB clients, Playwright/Chromium (README has the
exact recipe), extra language runtimes. FROM must match the pinned tag in the
generated docker-compose.yml (read it after init). Point at it with a build:
block in docker-compose.override.yml, not by editing docker-compose.yml.docker-compose.override.yml — env vars, host-service wiring
(DATABASE_URL: …@host.docker.internal:5432/… to reach a DB running on the
host; add the extra_hosts line on Linux), extra ports/mounts, the named-volume
declarations, and the Dockerfile.project build: block.vscode-extensions + vscode-settings.json — editor IntelliSense: the
language extension stack (Python → ms-python.python + ms-python.vscode-pylance
charliermarsh.ruff; TS → dbaeumer.vscode-eslint + esbenp.prettier-vscode;
etc.), interpreter path, format-on-save. Include the stop-auto-activating-.venv
settings for Python projects (otherwise VS Code types source .../activate into
the terminal and clobbers the AI CLI you just launched — README has the two keys).post-create.project.sh — runs as vscode, cwd /workspace, on every
create. Two jobs: (1) install the agent's LSP server binary on PATH so
Claude Code's go-to-def / find-refs tool works — this is separate from the
editor extensions and is the LSP piece people miss (Python → npm i -g pyright,
giving pyright-langserver; TS → npm i -g typescript typescript-language-server;
others in the references table); (2) project bootstrap (uv sync, npm ci,
lefthook install, pre-commit install, DB seed).firewall-allowlist — only if the user wants the stricter opt-in network
allowlist (reviewing untrusted code, corporate LAN). Off by default; mention it,
don't impose it.LSP is a first-class concern (the user cares about it). Make explicit that
there are two LSP surfaces: the editor's IntelliSense (extensions +
settings) and the agent's LSP tool (a language-server binary on PATH,
installed from post-create.project.sh). A project usually wants both. See the
references table for the binary + extension per language.
Show the user, before touching anything:
aic init invocation (with --with / --shell).aic up or VS Code
"Reopen in Container", then how to verify).Get an explicit yes. If they want changes, fold them in and re-show.
Order matters (so the override gets wired into dockerComposeFile):
aic init --with <tools> --shell <shell> for a fresh project,
or aic sync --with <tools> --shell <shell> if Step 0 found an existing aic
setup. (Both are non-interactive when you pass the flags.).devcontainer/docker-compose.yml if you're
writing a Dockerfile.project, and use it in its FROM.aic sync once more so the freshly-created
docker-compose.override.yml gets appended to dockerComposeFile in
devcontainer.json (it's wired only when the file is present). Verify with
grep dockerComposeFile .devcontainer/devcontainer.json — both files should
appear.Do not run aic up / aic rebuild (large image pull) — that's the user's
call.
Tell the user exactly what to run next and how to confirm it worked:
aic up (pulls the image, starts the stack), then aic shell,
then claude / codex / opencode.Cmd+Shift+P → Dev Containers: Reopen in Container.aic preflight prints exactly what crosses the boundary for this
config. After the container is up, check LSP servers are on PATH
(command -v pyright-langserver), the named volume is writable, and host
services resolve.aic signing if they sign commits (the host signing
key isn't forwarded); the firewall opt-in for stricter network containment.Keep the handoff concrete — these are copy-pasteable commands, not prose.
references/stack-and-suitability.md — stack-detection signal table, the full
devcontainer-suitability matrix, and the LSP-server-by-language table (editor
extension + agent binary). Read it during Steps 2, 4, and 5.npx claudepluginhub stefanoginella/aicontainer --plugin aicontainer-setupDefines standardized development environments or onboards developers by generating setup scripts, container configs, CI workflows, toolchain pins, and dev-setup documents.
Sets up and launches a Docker devcontainer running Claude Code with --dangerously-skip-permissions for autonomous sandboxed coding without prompts. Triggers on 'yolo' or 'autonomous mode'.
Creates devcontainers with language-specific tooling (Python/Node/Rust/Go) and persistent volumes. Use when adding devcontainer support to a project or setting up isolated development environments.