Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Creates, updates, or reviews a project's gen-env command for running multiple isolated instances on localhost. Handles instance identity, port allocation, data isolation, browser state separation, and cleanup.
.claude/skills/aiskillstore-gen-env/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-02 | ✗→✓ | ▲ Improved | 61% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 20% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 45% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 292% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 63% | 0% |
Generate or review a gen-env command that enables running multiple isolated instances of a project on localhost simultaneously (e.g., multiple worktrees, feature branches, or versions).
Without isolation, multiple instances of the same project:
docker down -v nukes everythingEverything flows from a workspace name:
name = "feature-x"
↓
┌─────────────────────────────────────────────────────┐
│ COMPOSE_PROJECT_NAME = localnet-feature-x │
│ DOCKER_NETWORK = localnet-feature-x │
│ VOLUME_PREFIX = localnet-feature-x │
│ CONTAINER_PREFIX = localnet-feature-x- │
│ TILT_HOST = feature-x.localhost │
│ Ports = dynamically allocated │
│ URLs = derived from host + ports │
└─────────────────────────────────────────────────────┘Each instance gets unique ports from ephemeral range (49152-65535).
Docker Compose project name controls volume naming:
localnet-main_postgres_datalocalnet-feature-x_postgres_dataNo cross-contamination. Independent databases.
Separate Docker networks per instance. Containers reference each other by service name without collision.
Critical: Different ports on localhost still share cookies!
http://localhost:3000 ─┐
├─ SAME cookies, localStorage
http://localhost:3001 ─┘Solution: subdomain isolation via *.localhost:
http://main.localhost:3000 ─ separate cookies
http://feature-x.localhost:3001 ─ separate cookiesChrome/Edge treat *.localhost as 127.0.0.1 automatically. No /etc/hosts needed.
Each instance can have its own auth realm/audience, preventing token confusion.
Clear prefixes on containers, volumes, Tilt resources, logs → know exactly which instance you're looking at.
When creating or reviewing gen-env:
Identity & Naming:
--name <workspace> argumentCOMPOSE_PROJECT_NAME from nameDOCKER_NETWORK, VOLUME_PREFIX, CONTAINER_PREFIX*_HOST for browser isolation (name.localhost)Port Allocation:
Persistence:
.gen-env.lock)--force regenerates all--clean removes generated filesOutput:
.localnet.env (or project-specific name)Integration:
.envrc.envrc--env-file)bash# .localnet.env - generated by gen-env # Instance: feature-x # Generated: 2024-01-15T10:30:00Z # === Instance Identity === WORKSPACE_NAME=feature-x COMPOSE_NAME=localnet-feature-x COMPOSE_PROJECT_NAME=localnet-feature-x DOCKER_NETWORK=localnet-feature-x VOLUME_PREFIX=localnet-feature-x CONTAINER_PREFIX=localnet-feature-x- # === Host (for browser isolation) === APP_HOST=feature-x.localhost TILT_HOST=feature-x.localhost # === Allocated Ports === POSTGRES_PORT=51234 REDIS_PORT=51235 API_PORT=51236 WEB_PORT=51237 # ... more ports # === Derived URLs === DATABASE_URL=postgres://user:pass@localhost:51234/dev WEB_URL=http://feature-x.localhost:51237 API_URL=http://feature-x.localhost:51236
bash# .envrc PATH_add bin # or scripts dotenv_if_exists .localnet.env
See @IMPLEMENTATION.md for full implementation.
Key types:
typescriptinterface InstanceConfig { name: string; // Workspace identity composeName: string; // Docker Compose project name dockerNetwork: string; // Docker network name volumePrefix: string; // Docker volume prefix containerPrefix: string; // Container name prefix host: string; // Browser hostname (name.localhost) ports: Record<string, number>; // Allocated ports urls: Record<string, string>; // Derived URLs } interface LockfileData { version: 1; generatedAt: string; instance: InstanceConfig; }
Surgical cleanup per instance:
bash# Clean only feature-x (containers + volumes + networks) docker compose -p localnet-feature-x down -v # Or via gen-env gen-env --clean # removes .localnet.env and .gen-env.lock # List all localnet instances docker ps -a --filter "name=localnet-" --format "table {{.Names}}\t{{.Status}}" # Nuclear option (all instances) - DANGEROUS docker ps -a --filter "name=localnet-" -q | xargs docker rm -f docker volume ls --filter "name=localnet-" -q | xargs docker volume rm
bash# Derive name from git worktree directory WORKTREE_NAME=$(basename "$(git rev-parse --show-toplevel)") gen-env --name "$WORKTREE_NAME"
bash# Derive name from branch BRANCH=$(git branch --show-current | tr '/' '-') gen-env --name "$BRANCH"
bash# User specifies (recommended for clarity) gen-env --name bb-dev gen-env --name testing-v2
When reviewing an existing gen-env:
*.localhost)localhost)❌ Hardcoded localhost in URLs
bashWEB_URL=http://localhost:${WEB_PORT} # BAD: shares cookies
✅ Use instance host
bashWEB_URL=http://${APP_HOST}:${WEB_PORT} # GOOD: isolated cookies
❌ No COMPOSE_PROJECT_NAME
bash# BAD: uses directory name, may conflict docker compose up
✅ Explicit project name
bashCOMPOSE_PROJECT_NAME=localnet-feature-x docker compose up # Uses project name for all resources
❌ Shared cleanup
bashdocker compose down -v # BAD: which instance?
✅ Instance-specific cleanup
bashdocker compose -p localnet-feature-x down -v # GOOD: explicit
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-06 | pass→pass | 11,433 | 11,771 | +3% | 1 | 1 | 0% | 2,174 | 4,154 | +91% | 0 | 0 | — |
case-05 | pass→pass | 12,179 | 12,588 | +3% | 1 | 1 | 0% | 2,290 | 4,508 | +97% | 0 | 0 | — |
case-01 | fail→fail | 31,345 | 26,436 | -16% | 1 | 1 | 0% | 5,941 | 6,825 | +15% | 0 | 0 | — |
case-02 | fail→pass | 24,094 | 23,593 | -2% | 1 | 1 | 0% | 4,141 | 6,670 | +61% | 0 | 0 | — |
case-03 | fail→pass | 21,524 | 13,102 | -39% | 1 | 1 | 0% | 3,714 | 4,459 | +20% | 0 | 0 | — |
case-04 | pass→pass | 15,644 | 16,491 | +5% | 1 | 1 | 0% | 3,063 | 5,744 | +88% | 0 | 0 | — |
case-07 | pass→pass | 16,062 | 29,078 | +81% | 1 | 1 | 0% | 2,595 | 4,769 | +84% | 0 | 0 | — |
case-08 | fail→pass | 13,597 | 9,913 | -27% | 1 | 1 | 0% | 2,392 | 3,479 | +45% | 0 | 0 | — |
case-09 | pass→pass | 12,298 | 5,901 | -52% | 1 | 1 | 0% | 2,029 | 2,918 | +44% | 0 | 0 | — |
case-10 | fail→pass | 4,474 | 3,625 | -19% | 1 | 1 | 0% | 646 | 2,532 | +292% | 0 | 0 | — |
case-11 | fail→pass | 18,322 | 15,679 | -14% | 1 | 1 | 0% | 2,940 | 4,797 | +63% | 0 | 0 | — |
case-12 | fail→pass | 6,851 | 4,345 | -37% | 1 | 1 | 0% | 1,106 | 2,783 | +152% | 0 | 0 | — |
case-13 | fail→pass | 17,016 | 13,764 | -19% | 1 | 1 | 0% | 2,799 | 4,561 | +63% | 0 | 0 | — |
case-14 | pass→pass | 11,763 | 6,871 | -42% | 1 | 1 | 0% | 1,918 | 3,255 | +70% | 0 | 0 | — |
case-15 | fail→pass | 19,978 | 3,458 | -83% | 1 | 1 | 0% | 926 | 2,646 | +186% | 0 | 0 | — |
case-16 | fail→pass | 13,401 | 9,834 | -27% | 1 | 1 | 0% | 2,158 | 3,565 | +65% | 0 | 0 | — |
case-17 | pass→pass | 9,982 | 7,524 | -25% | 1 | 1 | 0% | 1,844 | 3,327 | +80% | 0 | 0 | — |
case-18 | fail→fail | 12,531 | 8,370 | -33% | 1 | 1 | 0% | 2,328 | 3,631 | +56% | 0 | 0 | — |
case-19 | pass→pass | 16,413 | 12,162 | -26% | 1 | 1 | 0% | 2,888 | 4,290 | +49% | 0 | 0 | — |
case-20 | fail→pass | 11,039 | 7,204 | -35% | 1 | 1 | 0% | 1,891 | 3,447 | +82% | 0 | 0 | — |
case-21 | fail→pass | 15,972 | 15,439 | -3% | 1 | 1 | 0% | 2,814 | 4,716 | +68% | 0 | 0 | — |
case-22 | pass→pass | 13,769 | 11,928 | -13% | 1 | 1 | 0% | 2,458 | 4,123 | +68% | 0 | 0 | — |
case-23 | fail→fail | 6,312 | 5,085 | -19% | 1 | 1 | 0% | 976 | 2,873 | +194% | 0 | 0 | — |
case-24 | fail→pass | 9,966 | 8,923 | -10% | 1 | 1 | 0% | 1,748 | 3,689 | +111% | 0 | 0 | — |
case-25 | pass→pass | 12,190 | 12,191 | +0% | 1 | 1 | 0% | 1,941 | 4,064 | +109% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 25 cases were attempted, and 24 counted toward the lift figure. The other 1 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +48 percentage points is the difference between those two pass rates over the 24 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.