From bootstrap-worktrees
Set up project-agnostic worktree isolation with per-worktree ports, Docker Compose projects, and config files. Discovers services, generates create/remove scripts and Claude Code hook configuration. Triggers: /bootstrap-worktrees, set up worktree isolation, bootstrap worktrees
npx claudepluginhub benjamcalvin/bootstraps --plugin bootstrap-worktreesThis skill is limited to using the following tools:
Set up project-agnostic worktree isolation so multiple Claude Code sessions can work on different branches simultaneously without port conflicts, database collisions, or stale container leaks.
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Guides building MCP servers enabling LLMs to interact with external services via tools. Covers best practices, TypeScript/Node (MCP SDK), Python (FastMCP).
Generates original PNG/PDF visual art via design philosophy manifestos for posters, graphics, and static designs on user request.
Set up project-agnostic worktree isolation so multiple Claude Code sessions can work on different branches simultaneously without port conflicts, database collisions, or stale container leaks.
Before starting, gather context by running these commands:
cat .bootstraps-worktree-config.yml 2>/dev/null || echo "NO_CONFIG_FILE"ls scripts/create-worktree.sh scripts/remove-worktree.sh 2>/dev/null || echo "NO_SCRIPTS"ls compose.yml docker-compose.yml 2>/dev/null || echo "NO_COMPOSE"cat .claude/settings.json 2>/dev/null || echo "NO_SETTINGS"This skill runs six phases to set up worktree isolation. It is re-runnable — it checks what exists and only regenerates what's needed.
Template files are in the skill's assets/ directory at $SKILL_DIR/assets/. Read them on demand with the Read tool only when that phase needs them.
Check if .bootstraps-worktree-config.yml exists (from the context-gathering step above).
If NO_CONFIG_FILE: Proceed to Phase 1 (discovery) to build the config from scratch.
If config exists: Parse it and confirm with the user: "Found existing worktree config with N services. Re-run setup or modify?" Options:
Scan the project to discover services that need port isolation. Check these sources in order:
Look for compose.yml, docker-compose.yml, or compose.yaml in the project root and common subdirectories (docker/, infra/, .docker/). For each compose file found, extract:
services: blockports: entries (e.g., "5432:5432", "${DB_PORT}:5432")${DB_PORT:-5432})For each service with port mappings, create a service entry:
- name: <service_name> # e.g., "postgres", "redis"
env_var: <PORT_ENV_VAR> # e.g., "DB_PORT", "REDIS_PORT"
default_port: <number> # The port from the compose mapping
range_start: <number> # Start of allocation range
range_end: <number> # End of allocation range
Range allocation strategy: For each service, compute a 100-port range starting at the default port:
Scan .env, .env.example, .env.sample, .env.development for variables matching *_PORT patterns. Cross-reference with compose discoveries — add any ports found here that weren't in compose.
Scan for port references in common config files: mise.toml, mise.local.toml, Procfile, Makefile, docker-compose.override.yml. Add any new port env vars discovered.
Scan environment files for variables matching patterns that suggest external/shared resources:
*_URL, *_ENDPOINT — API endpoints*_API_KEY, *_SECRET — credentials*_BUCKET, *_QUEUE — cloud resources*_WEBHOOK — webhook URLsDATABASE_URL (when pointing to a remote host, not localhost)Record these as shared resources that cannot be isolated per-worktree.
If no services are discovered, inform the user: "No services with port bindings found. You can define services manually." Then proceed to Phase 2 with an empty service list for manual entry.
Present the discovered services to the user in a clear table:
Discovered services:
# | Service | Env Var | Default Port | Range
1 | postgres | DB_PORT | 5432 | 5432–5531
2 | redis | REDIS_PORT | 6379 | 6379–6478
3 | app-server | SERVER_PORT | 8080 | 8080–8179
Ask the user to confirm or modify:
Also present any shared resources discovered:
Shared resources detected (cannot be isolated per-worktree):
- DATABASE_URL → postgres://prod-host:5432/mydb (external database)
- STRIPE_API_KEY → sk_live_... (shared API key)
- S3_BUCKET → my-app-uploads (shared bucket)
Warn the user about each shared resource and ask if they want to note any mitigations (e.g., per-worktree schema prefix, test API keys).
Also ask:
<prefix>-<worktree-slug>.scripts/)..env (always generated)mise.local.toml — if mise.toml exists in the project<UPPER_REPO_NAME>_WORKTREE_DELETE_BRANCH).Construct the full configuration and save to .bootstraps-worktree-config.yml:
project_name: "<repo_name>"
compose_prefix: "<prefix>"
compose_file: "compose.yml"
scripts_dir: "scripts"
delete_branch_env_var: "MYPROJECT_WORKTREE_DELETE_BRANCH"
services:
- name: postgres
env_var: DB_PORT
default_port: 5432
range_start: 5432
range_end: 5531
- name: app-server
env_var: SERVER_PORT
default_port: 8080
range_start: 8080
range_end: 8179
config_formats:
- env # always
- mise.local.toml # if detected
shared_resources:
- name: DATABASE_URL
value: "postgres://prod-host:5432/mydb"
note: "Use per-worktree local database instead"
- name: STRIPE_API_KEY
note: "Use test keys per-worktree"
env_template: |
# Auto-generated by create-worktree.sh — do not commit.
{{ENV_LINES}}
COMPOSE_PROJECT_NAME=$compose_project
The env_template section captures the .env file content pattern. During discovery, build this from the existing .env or .env.example file, replacing hardcoded port values with the allocated port variables.
Read each template from $SKILL_DIR/assets/, replace the {{...}} placeholders with values derived from the configuration, and write to the scripts directory.
create-worktree.shRead $SKILL_DIR/assets/create-worktree.sh.tmpl. Replace these placeholders:
| Placeholder | Replacement |
|---|---|
{{SERVICE_PORT_ALLOCATIONS}} | One allocate_port call per service. Example for a service {name: postgres, env_var: DB_PORT, range_start: 5432, range_end: 5531}: db_port="$(allocate_port DB_PORT 5432 5531)" — use lowercase env_var as the bash variable name. |
{{PORT_LOG_SUMMARY}} | Space-separated KEY=$var pairs. Example: DB_PORT=$db_port SERVER_PORT=$server_port |
{{COMPOSE_PREFIX}} | The compose prefix from config (e.g., myproject) |
{{ENV_FILE_CONTENT}} | The .env content lines using allocated port variables. Build from the env_template in config, replacing {{ENV_LINES}} with one line per service env var (e.g., DB_PORT=$db_port) plus any additional static env vars from the project's .env.example. |
{{EXTRA_CONFIG_FILES}} | Additional config file generation blocks. For mise.local.toml: a heredoc writing [env] section with port assignments. For each format in config_formats (other than env). Include mise trust call if generating mise config. |
{{WORKTREE_INFO_PORTS}} | One KEY=$var line per service for the .worktree-info file. Example: DB_PORT=$db_port |
{{COMPOSE_FILE}} | The compose file path from config (e.g., compose.yml) |
{{COMPOSE_ENV_VARS}} | Environment variable assignments for the docker compose up command. Example: DB_PORT="$db_port" REDIS_PORT="$redis_port" |
{{SHARED_RESOURCES}} | A SHARED_RESOURCES block listing resources that can't be isolated, for operator awareness. Example: SHARED_RESOURCES=DATABASE_URL,STRIPE_API_KEY |
Write to <scripts_dir>/create-worktree.sh and make executable (chmod +x).
remove-worktree.shRead $SKILL_DIR/assets/remove-worktree.sh.tmpl. Replace:
| Placeholder | Replacement |
|---|---|
{{DELETE_BRANCH_ENV_VAR}} | The env var name from config (e.g., MYPROJECT_WORKTREE_DELETE_BRANCH) |
Write to <scripts_dir>/remove-worktree.sh and make executable.
After writing each script, verify:
bash -n <script> to check for syntax errorsRead $SKILL_DIR/assets/hooks-settings.json.tmpl. Replace {{SCRIPTS_DIR}} with the scripts directory from config (e.g., scripts).
Check if .claude/settings.json exists:
If it doesn't exist: Create .claude/ directory and write the hook config directly.
If it exists: Parse the existing settings. Check for existing WorktreeCreate and WorktreeRemove hooks:
hooks object (or create the hooks key if absent).After writing, confirm the hooks are correctly configured by re-reading the file and validating it's valid JSON.
Report to the user:
Generated scripts:
<scripts_dir>/create-worktree.sh — creates worktrees with isolated ports and compose projects<scripts_dir>/remove-worktree.sh — tears down worktrees and their resourcesHook configuration: .claude/settings.json updated with WorktreeCreate and WorktreeRemove hooks
Services configured: List each service with its env var and port range
Shared resource warnings: List any shared resources that need manual attention
Config saved: .bootstraps-worktree-config.yml — re-run /bootstrap-worktrees to regenerate
Next steps:
claude and then using the worktree featuremise.local.toml, .env, .worktree-info to .gitignore if not already present<DELETE_BRANCH_ENV_VAR>=1 to auto-delete merged branches on worktree removal/bootstrap-worktrees audit to verify the setup (available after installing the audit extension)Files to gitignore (suggest additions if not already present):
.env
mise.local.toml
.worktree-info