jpskill.com
📦 その他 コミュニティ

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本体の挙動とは独立した参考情報です。

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
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
🪟 Windows (PowerShell)
$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. 1. 下の青いボタンを押して gen-env.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → gen-env フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

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 は *.localhost127.0.0.1 として自動的に扱います。/etc/hosts は不要です。

5. 認証の隔離

各インスタンスは、独自の認証レルム/オーディエンスを持つことができ、トークンの混乱を防ぎます。

6. リソースの命名

コンテナ、ボリューム、Tilt リソース、ログに明確なプレフィックスを付ける → どのインスタンスを見ているのかを正確に把握できます。

実装チェックリスト

gen-env を作成またはレビューするとき:

識別と命名:

  • [ ] --name <workspace> 引数が必要
  • [ ] 名前を検証する (英数字 + ダッシュ、DNS の最大 63 文字)
  • [ ] 名前から COMPOSE_PROJECT_NAME を生成する
  • [ ] DOCKER_NETWORKVOLUME_PREFIXCONTAINER_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 -v nukes 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_NAME from name
  • [ ] Generates DOCKER_NETWORK, VOLUME_PREFIX, CONTAINER_PREFIX
  • [ ] Generates *_HOST for 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
  • [ ] --force regenerates all
  • [ ] --clean removes 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:

  1. Does it create instance identity? (not just ports)
  2. Does it set COMPOSE_PROJECT_NAME? (controls Docker naming)
  3. Does it generate a browser-safe host? (*.localhost)
  4. Are URLs derived with correct host? (not hardcoded localhost)
  5. Is cleanup surgical? (can remove one instance without affecting others)
  6. Does the lockfile store the name? (for consistency across runs)
  7. 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)