gen-env
ローカル環境で複数のプロジェクトインスタンスを分離して実行するためのgen-envコマンドを作成、更新、レビューし、インスタンスの識別、ポート割り当て、データ分離、ブラウザ状態の分離、およびクリーンアップを管理するSkill。
📜 元の英語説明(参考)
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.
🇯🇵 日本人クリエイター向け解説
ローカル環境で複数のプロジェクトインスタンスを分離して実行するためのgen-envコマンドを作成、更新、レビューし、インスタンスの識別、ポート割り当て、データ分離、ブラウザ状態の分離、およびクリーンアップを管理するSkill。
※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o gen-env.zip https://jpskill.com/download/16594.zip && unzip -o gen-env.zip && rm gen-env.zip
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/16594.zip -OutFile "$d\gen-env.zip"; Expand-Archive "$d\gen-env.zip" -DestinationPath $d -Force; ri "$d\gen-env.zip"
完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。
💾 手動でダウンロードしたい(コマンドが難しい人向け)
- 1. 下の青いボタンを押して
gen-env.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
gen-envフォルダができる - 3. そのフォルダを
C:\Users\あなたの名前\.claude\skills\(Win)または~/.claude/skills/(Mac)へ移動 - 4. Claude Code を再起動
⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。
🎯 このSkillでできること
下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。
📦 インストール方法 (3ステップ)
- 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
- 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
- 3. 展開してできたフォルダを、ホームフォルダの
.claude/skills/に置く- · macOS / Linux:
~/.claude/skills/ - · Windows:
%USERPROFILE%\.claude\skills\
- · macOS / Linux:
Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。
詳しい使い方ガイドを見る →- 最終更新
- 2026-05-18
- 取得日時
- 2026-05-18
- 同梱ファイル
- 1
📖 Skill本文(日本語訳)
※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。
gen-env Skill
gen-env コマンドを生成またはレビューし、localhost 上でプロジェクトの複数の隔離されたインスタンスを同時に実行できるようにします (例: 複数のワークツリー、フィーチャーブランチ、またはバージョン)。
問題点
隔離がない場合、同じプロジェクトの複数のインスタンスは次のようになります。
- ハードコードされたポート (3000, 5432, 8080) を奪い合う
- Docker ボリュームを共有 → データ破損
- ブラウザの Cookie/localStorage を共有 → 認証の混乱
- コンテナ名が曖昧 → どれがどれか分からない
- 壊滅的なクリーンアップのリスク →
docker down -vがすべてを消し去る
解決策: インスタンスの識別
すべてはワークスペース名から始まります。
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 = 動的に割り当て │
│ URLs = ホスト + ポートから派生 │
└─────────────────────────────────────────────────────┘
隔離の側面
1. ポートの隔離
各インスタンスは、エフェメラル範囲 (49152-65535) から一意のポートを取得します。
2. データの隔離
Docker Compose プロジェクト名がボリュームの命名を制御します。
- インスタンス A:
localnet-main_postgres_data - インスタンス B:
localnet-feature-x_postgres_data
相互汚染はありません。独立したデータベースです。
3. ネットワークの隔離
インスタンスごとに個別の Docker ネットワーク。コンテナは、衝突することなくサービス名で相互に参照します。
4. ブラウザの状態の隔離
重要: localhost 上の異なるポートは、依然として Cookie を共有します!
http://localhost:3000 ─┐
├─ 同じ Cookie、localStorage
http://localhost:3001 ─┘
解決策: *.localhost を介したサブドメインの隔離:
http://main.localhost:3000 ─ 個別の Cookie
http://feature-x.localhost:3001 ─ 個別の Cookie
Chrome/Edge は *.localhost を 127.0.0.1 として自動的に扱います。/etc/hosts は不要です。
5. 認証の隔離
各インスタンスは、独自の認証レルム/オーディエンスを持つことができ、トークンの混乱を防ぎます。
6. リソースの命名
コンテナ、ボリューム、Tilt リソース、ログに明確なプレフィックスを付ける → どのインスタンスを見ているのかを正確に把握できます。
実装チェックリスト
gen-env を作成またはレビューするとき:
識別と命名:
- [ ]
--name <workspace>引数が必要 - [ ] 名前を検証する (英数字 + ダッシュ、DNS の最大 63 文字)
- [ ] 名前から
COMPOSE_PROJECT_NAMEを生成する - [ ]
DOCKER_NETWORK、VOLUME_PREFIX、CONTAINER_PREFIXを生成する - [ ] ブラウザの隔離のために
*_HOSTを生成する (name.localhost)
ポートの割り当て:
- [ ] エフェメラル範囲 (49152-65535) から割り当てる
- [ ] 割り当てる前にポートの可用性を確認する
- [ ] CI 互換性のために短いタイムアウト (100ms) を使用する
- [ ] IPv6 が無効になっている環境を適切に処理する
永続性:
- [ ] ロックファイルに名前 + ポートを保存する (
.gen-env.lock) - [ ] ロックファイルが存在し、名前が一致する場合はポートを再利用する
- [ ]
--forceはすべてを再生成する - [ ]
--cleanは生成されたファイルを削除する
出力:
- [ ]
.localnet.env(またはプロジェクト固有の名前) を生成する - [ ] 生成タイムスタンプを含む明確なヘッダー
- [ ] すべての派生 URL は正しいホスト + ポートを使用する
統合:
- [ ] スクリプトが
.envrcを介して PATH に追加される - [ ] 生成された env が
.envrcによってソースされる - [ ] Docker Compose で動作する (
--env-file) - [ ] Tilt で動作する (Starlark が env ファイルを読み取る)
生成された環境の構造
# .localnet.env - gen-env によって生成されました
# インスタンス: feature-x
# 生成日時: 2024-01-15T10:30:00Z
# === インスタンスの識別 ===
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-
# === ホスト (ブラウザの隔離用) ===
APP_HOST=feature-x.localhost
TILT_HOST=feature-x.localhost
# === 割り当てられたポート ===
POSTGRES_PORT=51234
REDIS_PORT=51235
API_PORT=51236
WEB_PORT=51237
# ... その他のポート
# === 派生 URL ===
DATABASE_URL=postgres://user:pass@localhost:51234/dev
WEB_URL=http://feature-x.localhost:51237
API_URL=http://feature-x.localhost:51236
direnv の統合
# .envrc
PATH_add bin # または scripts
dotenv_if_exists .localnet.env
参照実装 (TypeScript/Bun)
完全な実装については、@IMPLEMENTATION.md を参照してください。
主要な型:
interface InstanceConfig {
name: string; // ワークスペースの識別
composeName: string; // Docker Compose プロジェクト名
dockerNetwork: string; // Docker ネットワーク名
volumePrefix: string; // Docker ボリュームのプレフィックス
containerPrefix: string; // コンテナ名のプレフィックス
host: string; // ブラウザのホスト名 (name.localhost)
ports: Record<string, number>; // 割り当てられたポート
urls: Record<string, string>; // 派生 URL
}
interface LockfileData {
version: 1;
generatedAt: string;
instance: InstanceConfig;
}
クリーンアップパターン
インスタンスごとの外科的なクリーンアップ:
# feature-x のみクリーンアップ (コンテナ + ボリューム + ネットワーク)
docker compose -p localnet-feature-x down -v
# または gen-env 経由
gen-env --clean # .localnet.env と .gen-env.lock を削除します
# すべての localnet インスタンスを一覧表示
docker ps -a --filter "name=localnet-" --format "table {{.Names}}\t{{.Status}}"
# 核オプション (すべてのインスタンス) - 危険
docker ps -a --filter "name=localnet-" -q | xargs docker rm -f
docker volume ls --filter "name=localnet-" -q | xargs docker volume rm
一般的なパターン
パターン 1: ワークツリーベースの命名
# git ワークツリーディレクトリから名前を派生させる
WORKTREE_NAME=$(basename "$(git rev-parse --show-toplevel)")
gen-env --name "$WORKTREE_NAME"
パターン 2: ブランチベースの命名
# ブランチから名前を派生させる
BRANCH=$(git branch --show-current | tr '/' '-')
gen-env --name "$BRANCH"
パターン 3: 明示的な名前
(原文がここで切り詰められています)
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開
gen-env Skill
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).
The Problem
Without isolation, multiple instances of the same project:
- Fight for hardcoded ports (3000, 5432, 8080)
- Share Docker volumes → data corruption
- Share browser cookies/localStorage → auth confusion
- Have ambiguous container names → can't tell which is which
- Risk catastrophic cleanup →
docker down -vnukes everything
The Solution: Instance Identity
Everything 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 │
└─────────────────────────────────────────────────────┘
Isolation Dimensions
1. Port Isolation
Each instance gets unique ports from ephemeral range (49152-65535).
2. Data Isolation
Docker Compose project name controls volume naming:
- Instance A:
localnet-main_postgres_data - Instance B:
localnet-feature-x_postgres_data
No cross-contamination. Independent databases.
3. Network Isolation
Separate Docker networks per instance. Containers reference each other by service name without collision.
4. Browser State Isolation
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 cookies
Chrome/Edge treat *.localhost as 127.0.0.1 automatically. No /etc/hosts needed.
5. Auth Isolation
Each instance can have its own auth realm/audience, preventing token confusion.
6. Resource Naming
Clear prefixes on containers, volumes, Tilt resources, logs → know exactly which instance you're looking at.
Implementation Checklist
When creating or reviewing gen-env:
Identity & Naming:
- [ ] Requires
--name <workspace>argument - [ ] Validates name (alphanumeric + dashes, max 63 chars for DNS)
- [ ] Generates
COMPOSE_PROJECT_NAMEfrom name - [ ] Generates
DOCKER_NETWORK,VOLUME_PREFIX,CONTAINER_PREFIX - [ ] Generates
*_HOSTfor browser isolation (name.localhost)
Port Allocation:
- [ ] Allocates from ephemeral range (49152-65535)
- [ ] Checks port availability before assignment
- [ ] Uses short timeout (100ms) for CI compatibility
- [ ] Handles IPv6-disabled environments gracefully
Persistence:
- [ ] Lockfile stores name + ports (
.gen-env.lock) - [ ] Reuses ports when lockfile exists and name matches
- [ ]
--forceregenerates all - [ ]
--cleanremoves generated files
Output:
- [ ] Generates
.localnet.env(or project-specific name) - [ ] Clear header with generation timestamp
- [ ] All derived URLs use correct host + port
Integration:
- [ ] Script added to PATH via
.envrc - [ ] Generated env sourced by
.envrc - [ ] Works with Docker Compose (
--env-file) - [ ] Works with Tilt (Starlark reads env file)
Generated Environment Structure
# .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
direnv Integration
# .envrc
PATH_add bin # or scripts
dotenv_if_exists .localnet.env
Reference Implementation (TypeScript/Bun)
See @IMPLEMENTATION.md for full implementation.
Key types:
interface 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;
}
Cleanup Patterns
Surgical cleanup per instance:
# 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
Common Patterns
Pattern 1: Worktree-Based Naming
# Derive name from git worktree directory
WORKTREE_NAME=$(basename "$(git rev-parse --show-toplevel)")
gen-env --name "$WORKTREE_NAME"
Pattern 2: Branch-Based Naming
# Derive name from branch
BRANCH=$(git branch --show-current | tr '/' '-')
gen-env --name "$BRANCH"
Pattern 3: Explicit Naming
# User specifies (recommended for clarity)
gen-env --name bb-dev
gen-env --name testing-v2
Review Checklist
When reviewing an existing gen-env:
- Does it create instance identity? (not just ports)
- Does it set COMPOSE_PROJECT_NAME? (controls Docker naming)
- Does it generate a browser-safe host? (
*.localhost) - Are URLs derived with correct host? (not hardcoded
localhost) - Is cleanup surgical? (can remove one instance without affecting others)
- Does the lockfile store the name? (for consistency across runs)
- Does it validate name conflicts? (warn if lockfile has different name)
Anti-Patterns
❌ Hardcoded localhost in URLs
WEB_URL=http://localhost:${WEB_PORT} # BAD: shares cookies
✅ Use instance host
WEB_URL=http://${APP_HOST}:${WEB_PORT} # GOOD: isolated cookies
❌ No COMPOSE_PROJECT_NAME
# BAD: uses directory name, may conflict
docker compose up
✅ Explicit project name
COMPOSE_PROJECT_NAME=localnet-feature-x
docker compose up # Uses project name for all resources
❌ Shared cleanup
docker compose down -v # BAD: which instance?
✅ Instance-specific cleanup
docker compose -p localnet-feature-x down -v # GOOD: explicit
References
- @IMPLEMENTATION.md - Full TypeScript implementation
- @ADVANCED_PATTERNS.md - Complex scenarios (monorepos, CI, Tilt integration)