@smicolon/ai-kit 0.2.1 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smicolon/ai-kit",
3
- "version": "0.2.1",
3
+ "version": "0.3.1",
4
4
  "description": "AI coding tool pack manager for Smicolon standards",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1,12 +1,33 @@
1
1
  # Changelog
2
2
 
3
- All notable changes to smi-worktree will be documented in this file.
3
+ All notable changes to worktree will be documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.2.0] - 2026-02-15
8
+
9
+ ### Added
10
+
11
+ - `.worktreeinclude` config file — gitignore-style patterns for files to copy
12
+ - Auto-generates `.worktreeinclude` with sensible defaults on first `wt create`
13
+ - `[rewrite]` section — auto-suffixes DB_NAME, DATABASE_URL, COMPOSE_PROJECT_NAME per branch
14
+ - `{{BRANCH}}` template support for custom env var rewriting
15
+ - `[docker]` section — generates `docker-compose.worktree.yml` with port offsets
16
+ - Deterministic port offset via branch name hash (1-100 range)
17
+ - Custom compose file path via `file=` directive (supports monorepo nested paths)
18
+ - Auto-creates Postgres databases (Docker containers or local)
19
+ - `docker compose down` on `wt remove` before removing worktree
20
+ - Port mapping summary in create output
21
+
7
22
  ### Changed
8
- - Renamed from `smi-worktree` to `worktree` as part of ai-kit migration
9
- - Moved from `plugins/smi-worktree/` to `packs/worktree/`
23
+
24
+ - `wt create` now uses `.worktreeinclude` patterns instead of hardcoded `.env*` copying
25
+ - `wt remove` stops Docker containers and notes that databases are preserved
26
+ - `wt help` documents `.worktreeinclude` sections
27
+
28
+ ### Removed
29
+
30
+ - Hardcoded `copy_env_files()` function (replaced by `.worktreeinclude` pattern matching)
10
31
 
11
32
  ## [0.1.0] - 2026-01-17
12
33
 
@@ -1,11 +1,14 @@
1
1
  # worktree
2
2
 
3
- Git worktree manager for parallel development with automatic environment setup.
3
+ Git worktree manager for parallel development with automatic environment isolation.
4
4
 
5
5
  ## Features
6
6
 
7
7
  - **Sibling naming**: `project--branch-name/` convention
8
- - **Auto env copy**: Copies `.env*` from root and nested directories
8
+ - **`.worktreeinclude`**: Configurable file copying (replaces hardcoded `.env*`)
9
+ - **Env rewriting**: Auto-suffixes DB names, URLs, and compose project names per branch
10
+ - **Docker isolation**: Port-offset overrides so worktrees don't conflict
11
+ - **Database auto-creation**: Creates Postgres databases for rewritten DB names
9
12
  - **Package manager detection**: bun → pnpm → yarn → npm
10
13
  - **Monorepo aware**: Detects workspaces, turbo, nx, lerna
11
14
  - **Editor integration**: Open in Cursor, Antigravity, or VS Code
@@ -16,6 +19,98 @@ Git worktree manager for parallel development with automatic environment setup.
16
19
  /plugin install worktree
17
20
  ```
18
21
 
22
+ ## Quick Start
23
+
24
+ ```bash
25
+ /wt create feature/auth
26
+ # → Creates worktree at ~/code/project--feature-auth/
27
+ # → Copies files per .worktreeinclude
28
+ # → Rewrites DB_NAME, DATABASE_URL with branch suffix
29
+ # → Generates docker-compose.worktree.yml with offset ports
30
+ # → Creates database in running Postgres
31
+ # → Installs dependencies
32
+ ```
33
+
34
+ ## `.worktreeinclude`
35
+
36
+ Auto-generated on first `wt create` if missing. Commit this file so your team shares the same config.
37
+
38
+ ```ini
39
+ # .worktreeinclude — Files to copy to new worktrees
40
+ .env*
41
+
42
+ # Monorepo (uncomment as needed)
43
+ # apps/*/.env*
44
+ # packages/*/.env*
45
+ # services/*/.env*
46
+
47
+ [rewrite]
48
+ auto
49
+
50
+ [docker]
51
+ auto
52
+ # file=apps/backend/docker-compose.local.yml
53
+ # port_offset=10
54
+ ```
55
+
56
+ ### File Patterns (top section)
57
+
58
+ Gitignore-style glob patterns for untracked files to copy from main worktree:
59
+
60
+ ```
61
+ .env*
62
+ apps/*/.env*
63
+ config/local.yml
64
+ ```
65
+
66
+ ### `[rewrite]` — Env Var Isolation
67
+
68
+ **`auto` mode** detects and suffixes known keys with a branch slug:
69
+
70
+ | Key | Example | Result |
71
+ |-----|---------|--------|
72
+ | `DB_NAME` | `myapp` | `myapp_feature_auth` |
73
+ | `POSTGRES_DB` | `myapp` | `myapp_feature_auth` |
74
+ | `DATABASE_URL` | `postgres://...host/myapp` | `postgres://...host/myapp_feature_auth` |
75
+ | `COMPOSE_PROJECT_NAME` | `myapp` | `myapp_feature_auth` |
76
+
77
+ **Template mode** uses `{{BRANCH}}` for custom vars:
78
+
79
+ ```bash
80
+ MY_CUSTOM_DB=app_{{BRANCH}}
81
+ REDIS_PREFIX={{BRANCH}}_
82
+ ```
83
+
84
+ Both modes work together — template takes precedence over auto.
85
+
86
+ Branch slug: `feature/auth-v2` → `feature_auth_v2` (lowercase, special chars → `_`, max 30 chars).
87
+
88
+ ### `[docker]` — Docker Compose Isolation
89
+
90
+ Generates a `docker-compose.worktree.yml` override with port offsets:
91
+
92
+ - **`auto`**: Auto-detects compose file (`local.yml` → `docker-compose.local.yml` → `docker-compose.yml` → etc.)
93
+ - **`file=path`**: Specify compose file path (supports nested monorepo paths)
94
+ - **`port_offset=N`**: Override the auto-calculated offset
95
+
96
+ ```ini
97
+ [docker]
98
+ auto
99
+ file=apps/backend/docker-compose.local.yml
100
+ ```
101
+
102
+ **How it works:**
103
+
104
+ 1. Parses ports from your compose file
105
+ 2. Calculates a deterministic offset from branch name (1-100 via `cksum`)
106
+ 3. Generates `docker-compose.worktree.yml` next to the original
107
+ 4. Sets `COMPOSE_FILE=original.yml:docker-compose.worktree.yml` in `.env`
108
+ 5. Sets `COMPOSE_PROJECT_NAME` for container/volume/network isolation
109
+
110
+ After setup, `docker compose up -d` in the worktree automatically picks up both files.
111
+
112
+ **Auto-creates databases** in running Postgres containers (Docker or local). Idempotent — safe to run multiple times.
113
+
19
114
  ## Usage
20
115
 
21
116
  ### Create Worktree
@@ -26,7 +121,10 @@ Git worktree manager for parallel development with automatic environment setup.
26
121
  ```
27
122
 
28
123
  Creates worktree at `~/code/project--feature-authentication/` with:
29
- - All `.env*` files copied
124
+ - Files copied per `.worktreeinclude`
125
+ - Env vars rewritten with branch suffix
126
+ - Docker port offsets applied
127
+ - Database created
30
128
  - Dependencies installed
31
129
 
32
130
  ### List Worktrees
@@ -43,6 +141,8 @@ Creates worktree at `~/code/project--feature-authentication/` with:
43
141
  /wt rm feat-payments -d # also delete branch
44
142
  ```
45
143
 
144
+ Stops Docker containers before removing. Databases are preserved (drop manually if no longer needed).
145
+
46
146
  ### Open in Editor
47
147
 
48
148
  ```bash
@@ -73,12 +173,6 @@ Priority: flag > `WT_EDITOR` > auto-detect (Cursor → Antigravity → VS Code)
73
173
 
74
174
  ## Auto-Setup Details
75
175
 
76
- ### Environment Files
77
-
78
- Copies from:
79
- - Root: `.env`, `.env.local`, `.env.development`, etc.
80
- - Nested: `apps/*/.env*`, `packages/*/.env*`, `services/*/.env*`
81
-
82
176
  ### Package Manager Detection
83
177
 
84
178
  Checks in order:
@@ -96,6 +190,19 @@ Detects via:
96
190
  - `lerna.json`
97
191
  - `"workspaces"` in `package.json`
98
192
 
193
+ ### Compose File Detection Order
194
+
195
+ When `[docker] auto` is set and no `file=` specified:
196
+
197
+ 1. `local.yml`
198
+ 2. `docker-compose.local.yml`
199
+ 3. `docker-compose.yml`
200
+ 4. `docker-compose.yaml`
201
+ 5. `compose.yml`
202
+ 6. `compose.yaml`
203
+
204
+ Also searches `apps/*/` and `services/*/` subdirectories.
205
+
99
206
  ## Skill Triggers
100
207
 
101
208
  The worktree-manager skill auto-activates when you mention:
@@ -104,6 +211,8 @@ The worktree-manager skill auto-activates when you mention:
104
211
  - "feature branch setup"
105
212
  - "work on multiple branches"
106
213
  - "separate workspace for branch"
214
+ - "docker port conflict" or "database isolation"
215
+ - "worktreeinclude" or "env isolation"
107
216
 
108
217
  ## License
109
218
 
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  name: wt
3
- description: Git worktree manager - create, list, remove, and open worktrees with automatic env copying and dependency installation
3
+ description: Git worktree manager - create, list, remove, and open worktrees with env isolation, Docker port offsets, and database auto-creation
4
4
  argument-hint: '<command> [branch] [options]'
5
5
  allowed-tools: ["Bash(${CLAUDE_PLUGIN_ROOT}/scripts/wt.sh:*)"]
6
6
  ---
7
7
 
8
8
  # Git Worktree Manager
9
9
 
10
- Manage git worktrees with automatic setup for parallel development.
10
+ Manage git worktrees with automatic isolation for parallel development.
11
11
 
12
12
  ## Usage
13
13
 
@@ -19,9 +19,9 @@ Manage git worktrees with automatic setup for parallel development.
19
19
 
20
20
  | Command | Alias | Description |
21
21
  |---------|-------|-------------|
22
- | `create <branch>` | `c` | Create worktree, copy .env files, install deps |
22
+ | `create <branch>` | `c` | Create worktree with isolation (env, docker, db) |
23
23
  | `list` | `ls` | List all worktrees for current repo |
24
- | `remove <branch>` | `rm` | Remove worktree (add `-d` to delete branch) |
24
+ | `remove <branch>` | `rm` | Remove worktree (stops Docker, add `-d` to delete branch) |
25
25
  | `open <branch> [--editor]` | `o` | Open worktree (--cursor\|-c, --agy\|-a, --code\|-v) |
26
26
 
27
27
  ## Instructions
@@ -35,7 +35,7 @@ Run the worktree manager script:
35
35
  ## Examples
36
36
 
37
37
  ```bash
38
- # Create worktree for new feature
38
+ # Create worktree for new feature (with full isolation)
39
39
  /wt create feature/authentication
40
40
 
41
41
  # Short form
@@ -44,7 +44,7 @@ Run the worktree manager script:
44
44
  # List all worktrees
45
45
  /wt ls
46
46
 
47
- # Remove worktree
47
+ # Remove worktree (stops Docker containers first)
48
48
  /wt rm feature/authentication
49
49
 
50
50
  # Remove worktree AND delete branch
@@ -67,7 +67,27 @@ Run the worktree manager script:
67
67
 
68
68
  ## Auto-Setup on Create
69
69
 
70
- 1. Copies all `.env*` files from root
71
- 2. Copies nested `.env*` from `apps/*/`, `packages/*/`, `services/*/`
72
- 3. Detects package manager (bun pnpm yarn → npm)
73
- 4. Runs install at root (monorepo-aware)
70
+ 1. Loads `.worktreeinclude` (generates default if missing)
71
+ 2. Copies files matching glob patterns
72
+ 3. Rewrites env vars (DB_NAME, DATABASE_URL, etc.) with branch suffix
73
+ 4. Generates `docker-compose.worktree.yml` with port offsets
74
+ 5. Auto-creates database in running Postgres
75
+ 6. Detects package manager (bun → pnpm → yarn → npm)
76
+ 7. Runs install at root (monorepo-aware)
77
+
78
+ ## `.worktreeinclude` Format
79
+
80
+ ```ini
81
+ # File patterns to copy
82
+ .env*
83
+ apps/*/.env*
84
+
85
+ [rewrite]
86
+ auto # suffix DB_NAME, DATABASE_URL, etc.
87
+ # MY_DB=app_{{BRANCH}} # template mode
88
+
89
+ [docker]
90
+ auto # auto-detect compose file
91
+ # file=apps/backend/docker-compose.local.yml
92
+ # port_offset=10
93
+ ```
@@ -11,6 +11,7 @@ RED='\033[0;31m'
11
11
  GREEN='\033[0;32m'
12
12
  YELLOW='\033[1;33m'
13
13
  BLUE='\033[0;34m'
14
+ CYAN='\033[0;36m'
14
15
  NC='\033[0m' # No Color
15
16
 
16
17
  # Helpers
@@ -58,66 +59,575 @@ detect_pkg_manager() {
58
59
  # Detect if monorepo
59
60
  is_monorepo() {
60
61
  local dir="${1:-$REPO_ROOT}"
61
- # Check for monorepo indicators
62
62
  [[ -f "$dir/pnpm-workspace.yaml" ]] && return 0
63
63
  [[ -f "$dir/turbo.json" ]] && return 0
64
64
  [[ -f "$dir/nx.json" ]] && return 0
65
65
  [[ -f "$dir/lerna.json" ]] && return 0
66
- # Check for workspaces in package.json
67
66
  if [[ -f "$dir/package.json" ]]; then
68
67
  grep -q '"workspaces"' "$dir/package.json" 2>/dev/null && return 0
69
68
  fi
70
69
  return 1
71
70
  }
72
71
 
73
- # Copy .env files from source to destination
74
- copy_env_files() {
72
+ # Get worktree path from branch name
73
+ get_worktree_path() {
74
+ local branch="$1"
75
+ local safe_branch="${branch//\//-}"
76
+ echo "${REPO_PARENT}/${REPO_NAME}--${safe_branch}"
77
+ }
78
+
79
+ # Strip main repo name prefix from worktree basename
80
+ get_branch_from_worktree() {
81
+ local wt_path="$1"
82
+ local wt_name=$(basename "$wt_path")
83
+ echo "${wt_name#${REPO_NAME}--}"
84
+ }
85
+
86
+ # =============================================================================
87
+ # Phase 1: .worktreeinclude Parsing & File Copying
88
+ # =============================================================================
89
+
90
+ # Generate default .worktreeinclude if it doesn't exist
91
+ generate_default_worktreeinclude() {
92
+ local target="$REPO_ROOT/.worktreeinclude"
93
+ [[ -f "$target" ]] && return 0
94
+
95
+ cat > "$target" << 'TEMPLATE'
96
+ # .worktreeinclude — Files to copy to new worktrees
97
+ # Commit this file so your team shares the same config
98
+ .env*
99
+
100
+ # Monorepo (uncomment as needed)
101
+ # apps/*/.env*
102
+ # packages/*/.env*
103
+ # services/*/.env*
104
+
105
+ [rewrite]
106
+ auto
107
+
108
+ [docker]
109
+ auto
110
+ # file=apps/backend/docker-compose.local.yml
111
+ # port_offset=10
112
+ TEMPLATE
113
+ info "Generated .worktreeinclude (commit this file)"
114
+ }
115
+
116
+ # Parse .worktreeinclude into 3 arrays: file patterns, rewrite lines, docker lines
117
+ parse_worktreeinclude() {
118
+ local include_file="$REPO_ROOT/.worktreeinclude"
119
+ WT_FILE_PATTERNS=()
120
+ WT_REWRITE_LINES=()
121
+ WT_DOCKER_LINES=()
122
+
123
+ [[ -f "$include_file" ]] || return 0
124
+
125
+ local current_section="files"
126
+ while IFS= read -r line || [[ -n "$line" ]]; do
127
+ # Strip trailing whitespace
128
+ line="${line%"${line##*[![:space:]]}"}"
129
+ # Skip empty lines and comments
130
+ [[ -z "$line" ]] && continue
131
+ [[ "$line" == \#* ]] && continue
132
+
133
+ # Detect section headers
134
+ if [[ "$line" == "[rewrite]" ]]; then
135
+ current_section="rewrite"
136
+ continue
137
+ elif [[ "$line" == "[docker]" ]]; then
138
+ current_section="docker"
139
+ continue
140
+ fi
141
+
142
+ case "$current_section" in
143
+ files) WT_FILE_PATTERNS+=("$line") ;;
144
+ rewrite) WT_REWRITE_LINES+=("$line") ;;
145
+ docker) WT_DOCKER_LINES+=("$line") ;;
146
+ esac
147
+ done < "$include_file"
148
+ }
149
+
150
+ # Expand glob patterns against main worktree, return matched files (relative paths)
151
+ match_files_by_patterns() {
152
+ local src="$1"
153
+ MATCHED_FILES=()
154
+
155
+ for pattern in "${WT_FILE_PATTERNS[@]}"; do
156
+ # Use subshell with nullglob to safely expand globs
157
+ while IFS= read -r -d '' file; do
158
+ local rel="${file#"$src"/}"
159
+ MATCHED_FILES+=("$rel")
160
+ done < <(
161
+ cd "$src"
162
+ shopt -s nullglob
163
+ shopt -s globstar 2>/dev/null || true # bash 4+ only, needed for **
164
+ for f in $pattern; do
165
+ [[ -f "$f" ]] && printf '%s\0' "$src/$f"
166
+ done
167
+ )
168
+ done
169
+ }
170
+
171
+ # Copy matched files preserving directory structure
172
+ copy_matched_files() {
75
173
  local src="$1"
76
174
  local dst="$2"
77
175
  local count=0
78
176
 
79
- # Copy root .env* files
80
- for envfile in "$src"/.env*; do
81
- [[ -f "$envfile" ]] || continue
82
- local filename=$(basename "$envfile")
83
- cp "$envfile" "$dst/$filename"
177
+ for rel in "${MATCHED_FILES[@]+"${MATCHED_FILES[@]}"}"; do
178
+ local dir=$(dirname "$rel")
179
+ [[ "$dir" != "." ]] && mkdir -p "$dst/$dir"
180
+ cp "$src/$rel" "$dst/$rel"
84
181
  ((count++))
85
182
  done
86
183
 
87
- # Copy nested .env* files from common monorepo directories
88
- for subdir in apps packages services libs modules; do
89
- [[ -d "$src/$subdir" ]] || continue
90
- for app_dir in "$src/$subdir"/*/; do
184
+ echo "$count"
185
+ }
186
+
187
+ # =============================================================================
188
+ # Phase 2: Env Var Rewriting
189
+ # =============================================================================
190
+
191
+ # Branch name → safe slug for suffixing (lowercase, special chars → _, max 30)
192
+ sanitize_branch_for_suffix() {
193
+ local branch="$1"
194
+ local slug
195
+ slug=$(printf '%s' "$branch" | tr '[:upper:]' '[:lower:]' | tr -c '[:alnum:]' '_')
196
+ # Trim leading/trailing underscores
197
+ slug="${slug#_}"
198
+ slug="${slug%_}"
199
+ # Truncate to 30 chars
200
+ printf '%s' "${slug:0:30}"
201
+ }
202
+
203
+ # Rewrite a single .env file — auto-detect known keys + template {{BRANCH}}
204
+ rewrite_env_file() {
205
+ local env_file="$1"
206
+ local branch_slug="$2"
207
+ [[ -f "$env_file" ]] || return 0
208
+ local has_auto=false
209
+
210
+ # Check if auto mode is enabled
211
+ for line in "${WT_REWRITE_LINES[@]}"; do
212
+ [[ "$line" == "auto" ]] && has_auto=true
213
+ done
214
+
215
+ local tmpfile
216
+ tmpfile=$(mktemp)
217
+ local changed=false
218
+
219
+ while IFS= read -r line || [[ -n "$line" ]]; do
220
+ local newline="$line"
221
+
222
+ # Template: replace {{BRANCH}} placeholders
223
+ if [[ "$line" == *'{{BRANCH}}'* ]]; then
224
+ newline="${line//\{\{BRANCH\}\}/$branch_slug}"
225
+ changed=true
226
+ elif [[ "$has_auto" == true ]]; then
227
+ # Auto-detect known keys (only lines with = that aren't comments)
228
+ if [[ "$line" =~ ^[[:space:]]*([A-Za-z_][A-Za-z0-9_]*)=(.*) ]]; then
229
+ local key="${BASH_REMATCH[1]}"
230
+ local val="${BASH_REMATCH[2]}"
231
+ # Strip surrounding quotes from val for processing
232
+ local raw_val="$val"
233
+ raw_val="${raw_val#\"}"
234
+ raw_val="${raw_val%\"}"
235
+ raw_val="${raw_val#\'}"
236
+ raw_val="${raw_val%\'}"
237
+
238
+ case "$key" in
239
+ DB_NAME|POSTGRES_DB|MYSQL_DATABASE|DATABASE_NAME)
240
+ # Suffix the value
241
+ newline="${key}=${raw_val}_${branch_slug}"
242
+ changed=true
243
+ ;;
244
+ DATABASE_URL|POSTGRES_URL)
245
+ # Parse URL: scheme://user:pass@host:port/dbname?params
246
+ # Suffix the database name portion
247
+ if [[ "$raw_val" =~ ^(.*://[^/]*/?)([^?]+)(.*) ]]; then
248
+ local prefix="${BASH_REMATCH[1]}"
249
+ local dbname="${BASH_REMATCH[2]}"
250
+ local suffix="${BASH_REMATCH[3]}"
251
+ newline="${key}=${prefix}${dbname}_${branch_slug}${suffix}"
252
+ changed=true
253
+ fi
254
+ ;;
255
+ COMPOSE_PROJECT_NAME)
256
+ newline="${key}=${raw_val}_${branch_slug}"
257
+ changed=true
258
+ ;;
259
+ esac
260
+ fi
261
+ fi
262
+
263
+ printf '%s\n' "$newline"
264
+ done < "$env_file" > "$tmpfile"
265
+
266
+ if [[ "$changed" == true ]]; then
267
+ mv "$tmpfile" "$env_file"
268
+ else
269
+ rm -f "$tmpfile"
270
+ fi
271
+
272
+ echo "$changed"
273
+ }
274
+
275
+ # Find and rewrite all .env* files in the worktree
276
+ rewrite_all_env_files() {
277
+ local wt_path="$1"
278
+ local branch_slug="$2"
279
+ local count=0
280
+
281
+ # Only proceed if there are rewrite lines configured
282
+ [[ ${#WT_REWRITE_LINES[@]} -gt 0 ]] || return 0
283
+
284
+ while IFS= read -r -d '' env_file; do
285
+ local basename_f
286
+ basename_f=$(basename "$env_file")
287
+ # Only process .env* files (not .envrc or similar non-env files)
288
+ [[ "$basename_f" == .env* ]] || continue
289
+ local result
290
+ result=$(rewrite_env_file "$env_file" "$branch_slug")
291
+ [[ "$result" == "true" ]] && ((count++))
292
+ done < <(find "$wt_path" -type f -name '.env*' -not -path '*/node_modules/*' -not -path '*/.git/*' -print0 2>/dev/null)
293
+
294
+ echo "$count"
295
+ }
296
+
297
+ # =============================================================================
298
+ # Phase 3: Docker Compose Isolation
299
+ # =============================================================================
300
+
301
+ # Deterministic port offset from branch name (1-100 range via cksum)
302
+ branch_to_port_offset() {
303
+ local branch="$1"
304
+ local hash
305
+ hash=$(printf '%s' "$branch" | cksum | awk '{print $1}')
306
+ echo $(( (hash % 100) + 1 ))
307
+ }
308
+
309
+ # Minimal YAML parser: extract host ports from docker-compose services
310
+ # Output format: service_name:host_port:container_port (one per line)
311
+ parse_docker_compose_ports() {
312
+ local compose_file="$1"
313
+ awk '
314
+ /^[[:space:]]*[a-zA-Z_][a-zA-Z0-9_-]*:/ && !/^\s*-/ && !/ports:/ && !/image:/ && !/container_name:/ {
315
+ # Top-level or nested service name
316
+ indent = 0
317
+ for (i = 1; i <= length($0); i++) {
318
+ if (substr($0, i, 1) == " ") indent++
319
+ else break
320
+ }
321
+ line = $0
322
+ gsub(/^[[:space:]]+/, "", line)
323
+ gsub(/:.*/, "", line)
324
+ if (indent <= 4) current_service = line
325
+ }
326
+ /ports:/ {
327
+ in_ports = 1
328
+ next
329
+ }
330
+ in_ports && /^[[:space:]]*-[[:space:]]*"?[0-9]/ {
331
+ line = $0
332
+ gsub(/^[[:space:]]*-[[:space:]]*/, "", line)
333
+ gsub(/"/, "", line)
334
+ gsub(/[[:space:]].*/, "", line)
335
+ # line is now like "5432:5432" or "8025:8025"
336
+ split(line, parts, ":")
337
+ if (length(parts) >= 2) {
338
+ print current_service ":" parts[1] ":" parts[2]
339
+ }
340
+ next
341
+ }
342
+ in_ports && /^[[:space:]]*[^-[:space:]]/ {
343
+ in_ports = 0
344
+ }
345
+ ' "$compose_file"
346
+ }
347
+
348
+ # Detect compose file path — checks [docker] file= directive, then auto-detects
349
+ detect_compose_file() {
350
+ local wt_path="$1"
351
+ local compose_file=""
352
+
353
+ # Check for file= directive in [docker] config
354
+ for line in "${WT_DOCKER_LINES[@]}"; do
355
+ if [[ "$line" =~ ^file=(.+) ]]; then
356
+ local specified="${BASH_REMATCH[1]}"
357
+ specified="${specified#"${specified%%[![:space:]]*}"}" # trim leading
358
+ specified="${specified%"${specified##*[![:space:]]}"}" # trim trailing
359
+ if [[ -f "$wt_path/$specified" ]]; then
360
+ echo "$specified"
361
+ return 0
362
+ else
363
+ warn "Specified compose file not found: $specified"
364
+ fi
365
+ fi
366
+ done
367
+
368
+ # Auto-detect in standard locations
369
+ local candidates=(
370
+ "local.yml"
371
+ "docker-compose.local.yml"
372
+ "docker-compose.yml"
373
+ "docker-compose.yaml"
374
+ "compose.yml"
375
+ "compose.yaml"
376
+ )
377
+
378
+ # Check root first
379
+ for candidate in "${candidates[@]}"; do
380
+ if [[ -f "$wt_path/$candidate" ]]; then
381
+ echo "$candidate"
382
+ return 0
383
+ fi
384
+ done
385
+
386
+ # Check nested app directories
387
+ for subdir in apps services; do
388
+ [[ -d "$wt_path/$subdir" ]] || continue
389
+ for app_dir in "$wt_path/$subdir"/*/; do
91
390
  [[ -d "$app_dir" ]] || continue
92
- local app_name=$(basename "$app_dir")
93
- for envfile in "$app_dir".env*; do
94
- [[ -f "$envfile" ]] || continue
95
- local filename=$(basename "$envfile")
96
- # Ensure target directory exists
97
- mkdir -p "$dst/$subdir/$app_name"
98
- cp "$envfile" "$dst/$subdir/$app_name/$filename"
99
- ((count++))
391
+ for candidate in "${candidates[@]}"; do
392
+ if [[ -f "$app_dir$candidate" ]]; then
393
+ local rel="${app_dir#"$wt_path"/}$candidate"
394
+ echo "$rel"
395
+ return 0
396
+ fi
100
397
  done
101
398
  done
102
399
  done
103
400
 
104
- echo "$count"
401
+ return 1
105
402
  }
106
403
 
107
- # Get worktree path from branch name
108
- get_worktree_path() {
109
- local branch="$1"
110
- # Replace slashes with dashes for branch names like feature/auth
111
- local safe_branch="${branch//\//-}"
112
- echo "${REPO_PARENT}/${REPO_NAME}--${safe_branch}"
404
+ # Generate docker-compose.worktree.yml override with port offsets
405
+ generate_docker_compose_override() {
406
+ local compose_file="$1" # full path to original compose file
407
+ local offset="$2"
408
+ local override_dir
409
+ override_dir=$(dirname "$compose_file")
410
+ local override_file="$override_dir/docker-compose.worktree.yml"
411
+
412
+ local ports_data
413
+ ports_data=$(parse_docker_compose_ports "$compose_file")
414
+
415
+ [[ -z "$ports_data" ]] && return 1
416
+
417
+ local tmpfile
418
+ tmpfile=$(mktemp)
419
+
420
+ cat > "$tmpfile" << 'HEADER'
421
+ # Auto-generated by wt — DO NOT EDIT
422
+ # Port offsets for worktree isolation
423
+ services:
424
+ HEADER
425
+
426
+ local current_service=""
427
+ while IFS=: read -r service host_port container_port; do
428
+ [[ -z "$service" ]] && continue
429
+ local new_port=$((host_port + offset))
430
+
431
+ if [[ "$service" != "$current_service" ]]; then
432
+ printf ' %s:\n' "$service" >> "$tmpfile"
433
+ printf ' ports:\n' >> "$tmpfile"
434
+ current_service="$service"
435
+ fi
436
+ printf ' - "%d:%s"\n' "$new_port" "$container_port" >> "$tmpfile"
437
+ done <<< "$ports_data"
438
+
439
+ mv "$tmpfile" "$override_file"
440
+ echo "$override_file"
113
441
  }
114
442
 
115
- # Strip main repo name prefix from worktree basename
116
- get_branch_from_worktree() {
443
+ # Orchestrate Docker isolation: detect, offset, generate override, update .env
444
+ setup_docker_isolation() {
117
445
  local wt_path="$1"
118
- local wt_name=$(basename "$wt_path")
119
- # Remove the repo name prefix and --
120
- echo "${wt_name#${REPO_NAME}--}"
446
+ local branch="$2"
447
+ local branch_slug="$3"
448
+
449
+ # Check if docker section has 'auto' or any config
450
+ local has_docker=false
451
+ for line in "${WT_DOCKER_LINES[@]}"; do
452
+ [[ "$line" == "auto" || "$line" =~ ^file= ]] && has_docker=true
453
+ done
454
+ [[ "$has_docker" == true ]] || return 0
455
+
456
+ local compose_rel
457
+ compose_rel=$(detect_compose_file "$wt_path") || {
458
+ warn "No docker-compose file found, skipping Docker isolation"
459
+ return 0
460
+ }
461
+
462
+ local compose_full="$wt_path/$compose_rel"
463
+ local offset
464
+ offset=$(branch_to_port_offset "$branch")
465
+
466
+ # Check for custom port_offset
467
+ for line in "${WT_DOCKER_LINES[@]}"; do
468
+ if [[ "$line" =~ ^port_offset=([0-9]+) ]]; then
469
+ offset="${BASH_REMATCH[1]}"
470
+ fi
471
+ done
472
+
473
+ info "Docker isolation: $compose_rel (port offset +$offset)"
474
+
475
+ local override_file
476
+ override_file=$(generate_docker_compose_override "$compose_full" "$offset") || {
477
+ warn "No ports found in compose file, skipping port override"
478
+ return 0
479
+ }
480
+
481
+ local override_rel="${override_file#"$wt_path"/}"
482
+ success "Generated $override_rel"
483
+
484
+ # Set COMPOSE_FILE and COMPOSE_PROJECT_NAME in root .env
485
+ local root_env="$wt_path/.env"
486
+ local compose_file_val="${compose_rel}:${override_rel}"
487
+ local project_name="${REPO_NAME}_${branch_slug}"
488
+
489
+ # Append or update COMPOSE_FILE in .env
490
+ if [[ -f "$root_env" ]]; then
491
+ local tmpfile
492
+ tmpfile=$(mktemp)
493
+ local found_cf=false
494
+ local found_cpn=false
495
+ while IFS= read -r line || [[ -n "$line" ]]; do
496
+ if [[ "$line" =~ ^COMPOSE_FILE= ]]; then
497
+ printf 'COMPOSE_FILE=%s\n' "$compose_file_val"
498
+ found_cf=true
499
+ elif [[ "$line" =~ ^COMPOSE_PROJECT_NAME= ]]; then
500
+ printf 'COMPOSE_PROJECT_NAME=%s\n' "$project_name"
501
+ found_cpn=true
502
+ else
503
+ printf '%s\n' "$line"
504
+ fi
505
+ done < "$root_env" > "$tmpfile"
506
+ [[ "$found_cf" == false ]] && printf 'COMPOSE_FILE=%s\n' "$compose_file_val" >> "$tmpfile"
507
+ [[ "$found_cpn" == false ]] && printf 'COMPOSE_PROJECT_NAME=%s\n' "$project_name" >> "$tmpfile"
508
+ mv "$tmpfile" "$root_env"
509
+ else
510
+ printf 'COMPOSE_FILE=%s\n' "$compose_file_val" > "$root_env"
511
+ printf 'COMPOSE_PROJECT_NAME=%s\n' "$project_name" >> "$root_env"
512
+ fi
513
+
514
+ # Print port mapping summary
515
+ local ports_data
516
+ ports_data=$(parse_docker_compose_ports "$compose_full")
517
+ if [[ -n "$ports_data" ]]; then
518
+ echo ""
519
+ echo -e " ${CYAN}Port mappings:${NC}"
520
+ while IFS=: read -r service host_port container_port; do
521
+ [[ -z "$service" ]] && continue
522
+ local new_port=$((host_port + offset))
523
+ echo -e " ${service}: ${host_port} → ${GREEN}${new_port}${NC}"
524
+ done <<< "$ports_data"
525
+ fi
526
+ }
527
+
528
+ # =============================================================================
529
+ # Phase 4: Database Auto-Creation
530
+ # =============================================================================
531
+
532
+ # Detect Postgres — checks Docker containers first, then local
533
+ detect_postgres() {
534
+ # Check Docker containers for postgres
535
+ if command -v docker &>/dev/null; then
536
+ local pg_container
537
+ pg_container=$(docker ps --filter "ancestor=postgres" --filter "status=running" --format '{{.Names}}' 2>/dev/null | head -1)
538
+ if [[ -n "$pg_container" ]]; then
539
+ echo "docker:$pg_container"
540
+ return 0
541
+ fi
542
+ # Also check by port binding (for custom images)
543
+ pg_container=$(docker ps --filter "publish=5432" --filter "status=running" --format '{{.Names}}' 2>/dev/null | head -1)
544
+ if [[ -n "$pg_container" ]]; then
545
+ echo "docker:$pg_container"
546
+ return 0
547
+ fi
548
+ fi
549
+
550
+ # Check local postgres
551
+ if command -v pg_isready &>/dev/null && pg_isready -q 2>/dev/null; then
552
+ echo "local"
553
+ return 0
554
+ fi
555
+
556
+ return 1
557
+ }
558
+
559
+ # Create database if it doesn't exist (idempotent)
560
+ create_database_if_not_exists() {
561
+ local db_name="$1"
562
+ local pg_source="$2" # "docker:container_name" or "local"
563
+
564
+ local exists_query="SELECT 1 FROM pg_database WHERE datname='$db_name'"
565
+ local create_query="CREATE DATABASE \"$db_name\""
566
+ local result=""
567
+
568
+ if [[ "$pg_source" == local ]]; then
569
+ result=$(psql -tAc "$exists_query" postgres 2>/dev/null || true)
570
+ if [[ "$result" != "1" ]]; then
571
+ psql -c "$create_query" postgres 2>/dev/null && return 0 || return 1
572
+ fi
573
+ elif [[ "$pg_source" == docker:* ]]; then
574
+ local container="${pg_source#docker:}"
575
+ result=$(docker exec "$container" psql -U postgres -tAc "$exists_query" postgres 2>/dev/null || true)
576
+ if [[ "$result" != "1" ]]; then
577
+ docker exec "$container" psql -U postgres -c "$create_query" postgres 2>/dev/null && return 0 || return 1
578
+ fi
579
+ fi
580
+
581
+ return 0 # Already exists
582
+ }
583
+
584
+ # Read rewritten DB name from .env files and auto-create databases
585
+ auto_create_databases() {
586
+ local wt_path="$1"
587
+ local pg_source
588
+
589
+ pg_source=$(detect_postgres) || {
590
+ warn "Postgres not running, skipping database auto-creation"
591
+ return 0
592
+ }
593
+
594
+ # Collect unique database names from all .env files
595
+ local db_names=()
596
+ while IFS= read -r -d '' env_file; do
597
+ while IFS= read -r line; do
598
+ if [[ "$line" =~ ^(DB_NAME|POSTGRES_DB|MYSQL_DATABASE|DATABASE_NAME)=(.+) ]]; then
599
+ local val="${BASH_REMATCH[2]}"
600
+ val="${val#\"}" ; val="${val%\"}"
601
+ val="${val#\'}" ; val="${val%\'}"
602
+ # Only add if not already in array
603
+ local found=false
604
+ for existing in "${db_names[@]+"${db_names[@]}"}"; do
605
+ [[ "$existing" == "$val" ]] && found=true
606
+ done
607
+ [[ "$found" == false ]] && db_names+=("$val")
608
+ elif [[ "$line" =~ ^(DATABASE_URL|POSTGRES_URL)=(.+) ]]; then
609
+ local url="${BASH_REMATCH[2]}"
610
+ url="${url#\"}" ; url="${url%\"}"
611
+ # Extract db name from URL: scheme://user:pass@host:port/dbname
612
+ if [[ "$url" =~ ://[^/]*/([^?]+) ]]; then
613
+ local val="${BASH_REMATCH[1]}"
614
+ local found=false
615
+ for existing in "${db_names[@]+"${db_names[@]}"}"; do
616
+ [[ "$existing" == "$val" ]] && found=true
617
+ done
618
+ [[ "$found" == false ]] && db_names+=("$val")
619
+ fi
620
+ fi
621
+ done < "$env_file"
622
+ done < <(find "$wt_path" -maxdepth 3 -name '.env*' -not -path '*/node_modules/*' -not -path '*/.git/*' -print0 2>/dev/null)
623
+
624
+ for db_name in "${db_names[@]+"${db_names[@]}"}"; do
625
+ if create_database_if_not_exists "$db_name" "$pg_source"; then
626
+ success "Database ready: $db_name"
627
+ else
628
+ warn "Could not create database: $db_name"
629
+ fi
630
+ done
121
631
  }
122
632
 
123
633
  # =============================================================================
@@ -141,9 +651,9 @@ cmd_create() {
141
651
  exit 1
142
652
  fi
143
653
 
654
+ # --- Step 1: Create git worktree ---
144
655
  info "Creating worktree for branch: $branch"
145
656
 
146
- # Check if branch exists (local or remote)
147
657
  if git show-ref --verify --quiet "refs/heads/$branch" 2>/dev/null; then
148
658
  info "Checking out existing local branch: $branch"
149
659
  git worktree add "$wt_path" "$branch"
@@ -156,16 +666,47 @@ cmd_create() {
156
666
  fi
157
667
  success "Worktree created at: $wt_path"
158
668
 
159
- # Copy .env files
160
- info "Copying .env files..."
161
- local env_count=$(copy_env_files "$REPO_ROOT" "$wt_path")
162
- if [[ "$env_count" -gt 0 ]]; then
163
- success "Copied $env_count .env file(s)"
164
- else
165
- warn "No .env files found to copy"
669
+ # --- Step 2: Load .worktreeinclude ---
670
+ generate_default_worktreeinclude
671
+ parse_worktreeinclude
672
+
673
+ # --- Step 3: Copy files matching patterns ---
674
+ if [[ ${#WT_FILE_PATTERNS[@]} -gt 0 ]]; then
675
+ info "Copying files from .worktreeinclude..."
676
+ match_files_by_patterns "$REPO_ROOT"
677
+ local file_count
678
+ file_count=$(copy_matched_files "$REPO_ROOT" "$wt_path")
679
+ if [[ "$file_count" -gt 0 ]]; then
680
+ success "Copied $file_count file(s)"
681
+ else
682
+ warn "No matching files found to copy"
683
+ fi
166
684
  fi
167
685
 
168
- # Detect and run package manager install
686
+ # --- Step 4: Rewrite env vars ---
687
+ local branch_slug
688
+ branch_slug=$(sanitize_branch_for_suffix "$branch")
689
+
690
+ if [[ ${#WT_REWRITE_LINES[@]} -gt 0 ]]; then
691
+ info "Rewriting env vars (suffix: _$branch_slug)..."
692
+ local rewrite_count
693
+ rewrite_count=$(rewrite_all_env_files "$wt_path" "$branch_slug")
694
+ if [[ "$rewrite_count" -gt 0 ]]; then
695
+ success "Rewrote $rewrite_count .env file(s)"
696
+ fi
697
+ fi
698
+
699
+ # --- Step 5: Docker Compose isolation ---
700
+ if [[ ${#WT_DOCKER_LINES[@]} -gt 0 ]]; then
701
+ setup_docker_isolation "$wt_path" "$branch" "$branch_slug"
702
+ fi
703
+
704
+ # --- Step 6: Auto-create database ---
705
+ if [[ ${#WT_REWRITE_LINES[@]} -gt 0 ]]; then
706
+ auto_create_databases "$wt_path"
707
+ fi
708
+
709
+ # --- Step 7: Install dependencies ---
169
710
  local pkg_mgr=$(detect_pkg_manager "$wt_path")
170
711
  if [[ -n "$pkg_mgr" ]]; then
171
712
  info "Detected package manager: $pkg_mgr"
@@ -185,6 +726,7 @@ cmd_create() {
185
726
  success "Dependencies installed"
186
727
  fi
187
728
 
729
+ # --- Step 8: Summary ---
188
730
  echo ""
189
731
  success "Worktree ready!"
190
732
  echo ""
@@ -197,10 +739,8 @@ cmd_list() {
197
739
  info "Worktrees for: $REPO_NAME"
198
740
  echo ""
199
741
 
200
- # Get all worktrees
201
742
  local found=0
202
743
  while IFS= read -r line; do
203
- # Parse worktree output (format: "path HEAD branch")
204
744
  local wt_path=$(echo "$line" | awk '{print $1}')
205
745
  local wt_branch=$(echo "$line" | awk '{print $3}' | sed 's/\[//;s/\]//')
206
746
 
@@ -223,7 +763,6 @@ cmd_remove() {
223
763
  local branch="${1:-}"
224
764
  local delete_branch=false
225
765
 
226
- # Check for --delete-branch flag
227
766
  if [[ "${2:-}" == "--delete-branch" ]] || [[ "${2:-}" == "-d" ]]; then
228
767
  delete_branch=true
229
768
  fi
@@ -243,9 +782,23 @@ cmd_remove() {
243
782
  exit 1
244
783
  fi
245
784
 
785
+ # Stop Docker containers if compose setup exists
786
+ if [[ -f "$wt_path/.env" ]]; then
787
+ local compose_file_val=""
788
+ while IFS= read -r line; do
789
+ [[ "$line" =~ ^COMPOSE_FILE=(.+) ]] && compose_file_val="${BASH_REMATCH[1]}"
790
+ done < "$wt_path/.env"
791
+
792
+ if [[ -n "$compose_file_val" ]] && command -v docker &>/dev/null; then
793
+ info "Stopping Docker containers..."
794
+ (cd "$wt_path" && docker compose down 2>/dev/null) || warn "Could not stop containers (may not be running)"
795
+ fi
796
+ fi
797
+
246
798
  info "Removing worktree: $wt_path"
247
799
  git worktree remove "$wt_path" --force
248
800
  success "Worktree removed"
801
+ info "Note: databases are preserved — drop manually if no longer needed"
249
802
 
250
803
  if [[ "$delete_branch" == true ]]; then
251
804
  info "Deleting branch: $branch"
@@ -258,7 +811,6 @@ cmd_open() {
258
811
  local branch=""
259
812
  local editor=""
260
813
 
261
- # Parse arguments
262
814
  while [[ $# -gt 0 ]]; do
263
815
  case "$1" in
264
816
  --cursor|-c)
@@ -299,7 +851,6 @@ cmd_open() {
299
851
  exit 1
300
852
  fi
301
853
 
302
- # Priority: flag > WT_EDITOR env > auto-detect
303
854
  if [[ -z "$editor" && -n "${WT_EDITOR:-}" ]]; then
304
855
  editor="$WT_EDITOR"
305
856
  fi
@@ -312,7 +863,6 @@ cmd_open() {
312
863
  info "Opening in $editor: $wt_path"
313
864
  "$editor" "$wt_path"
314
865
  else
315
- # Auto-detect: Cursor → Antigravity → VS Code
316
866
  if command -v cursor &>/dev/null; then
317
867
  info "Opening in Cursor: $wt_path"
318
868
  cursor "$wt_path"
@@ -336,7 +886,7 @@ cmd_help() {
336
886
  echo "Usage: wt <command> [args]"
337
887
  echo ""
338
888
  echo "Commands:"
339
- echo " create, c <branch> Create worktree, copy .env files, install deps"
889
+ echo " create, c <branch> Create worktree with isolation (env, docker, db)"
340
890
  echo " list, ls List all worktrees for current repo"
341
891
  echo " remove, rm <branch> Remove worktree (add -d to delete branch too)"
342
892
  echo " open, o <branch> Open worktree (flag > WT_EDITOR > auto-detect)"
@@ -346,6 +896,17 @@ cmd_help() {
346
896
  echo "Environment:"
347
897
  echo " WT_EDITOR Default editor (cursor, agy, code)"
348
898
  echo ""
899
+ echo "Isolation (.worktreeinclude):"
900
+ echo " Auto-generated on first 'wt create' if missing."
901
+ echo " Controls which files to copy and how to isolate services."
902
+ echo ""
903
+ echo " Sections:"
904
+ echo " (top) Glob patterns for files to copy (e.g., .env*)"
905
+ echo " [rewrite] 'auto' to suffix DB_NAME, DATABASE_URL, etc."
906
+ echo " Use {{BRANCH}} for custom templates"
907
+ echo " [docker] 'auto' to generate port-offset override"
908
+ echo " 'file=path' for custom compose file"
909
+ echo ""
349
910
  echo "Naming convention:"
350
911
  echo " ~/code/project/ → main repo"
351
912
  echo " ~/code/project--feat-auth/ → worktree for feat-auth branch"
@@ -1,12 +1,12 @@
1
1
  ---
2
2
  name: worktree-manager
3
- description: This skill should be used when the user mentions "worktree", "wt", "new branch workspace", "parallel development", "feature branch setup", "work on multiple branches", "separate workspace for branch", or wants to manage git worktrees for parallel feature development.
4
- version: 1.0.0
3
+ description: This skill should be used when the user mentions "worktree", "wt", "new branch workspace", "parallel development", "feature branch setup", "work on multiple branches", "separate workspace for branch", "docker port conflict", "database isolation", "worktreeinclude", "env isolation", or wants to manage git worktrees for parallel feature development.
4
+ version: 2.0.0
5
5
  ---
6
6
 
7
7
  # Git Worktree Manager Skill
8
8
 
9
- Manages git worktrees for parallel development with automatic environment setup.
9
+ Manages git worktrees for parallel development with automatic environment isolation.
10
10
 
11
11
  ## Activation Triggers
12
12
 
@@ -17,14 +17,17 @@ This skill activates when the user:
17
17
  - Needs a "separate workspace for a feature"
18
18
  - Asks about "parallel development" setup
19
19
  - Wants to "set up a new branch workspace"
20
+ - Has "docker port conflicts" between branches
21
+ - Needs "database isolation" for parallel work
22
+ - Asks about ".worktreeinclude" or "env isolation"
20
23
 
21
24
  ## Commands Reference
22
25
 
23
26
  | Command | Alias | Description |
24
27
  |---------|-------|-------------|
25
- | `/wt create <branch>` | `/wt c` | Create worktree with auto-setup |
28
+ | `/wt create <branch>` | `/wt c` | Create worktree with full isolation |
26
29
  | `/wt list` | `/wt ls` | Show all worktrees |
27
- | `/wt remove <branch>` | `/wt rm` | Remove worktree |
30
+ | `/wt remove <branch>` | `/wt rm` | Remove worktree (stops Docker first) |
28
31
  | `/wt open <branch> [--editor]` | `/wt o` | Open (--cursor\|-c, --agy\|-a, --code\|-v) |
29
32
 
30
33
  ## Naming Convention
@@ -36,23 +39,55 @@ Worktrees are created as siblings with `--` separator:
36
39
  ~/[PARENT_DIRECTORY]/[REPO_NAME]--[BRANCH_NAME]/ # worktree for [BRANCH_NAME]
37
40
  ```
38
41
 
42
+ ## Three-Layer Isolation
43
+
44
+ ### Layer 1: `.worktreeinclude` — File Selection
45
+
46
+ Gitignore-style file at repo root controlling which untracked files to copy. Auto-generated with sensible defaults on first `wt create`.
47
+
48
+ ```ini
49
+ .env*
50
+ # apps/*/.env*
51
+
52
+ [rewrite]
53
+ auto
54
+
55
+ [docker]
56
+ auto
57
+ ```
58
+
59
+ ### Layer 2: `[rewrite]` — Env Var Isolation
60
+
61
+ - **`auto`**: Suffixes DB_NAME, POSTGRES_DB, DATABASE_URL, COMPOSE_PROJECT_NAME with branch slug
62
+ - **`{{BRANCH}}`**: Template placeholder for custom vars
63
+
64
+ ### Layer 3: `[docker]` — Docker Compose Override
65
+
66
+ - Generates `docker-compose.worktree.yml` with port offsets
67
+ - Sets `COMPOSE_FILE` and `COMPOSE_PROJECT_NAME` in `.env`
68
+ - Auto-creates databases in running Postgres
69
+
39
70
  ## Auto-Setup Features
40
71
 
41
72
  When creating a worktree, the following happens automatically:
42
73
 
43
74
  1. **Branch handling**: Creates new branch or checks out existing (local/remote)
44
- 2. **Env files**: Copies all `.env*` from root and nested directories
45
- 3. **Package manager**: Detects bun/pnpm/yarn/npm from lockfiles
46
- 4. **Dependencies**: Runs install at root (monorepo-aware)
75
+ 2. **File copying**: Copies files matching `.worktreeinclude` patterns
76
+ 3. **Env rewriting**: Suffixes database names and URLs with branch slug
77
+ 4. **Docker isolation**: Generates port-offset override, sets compose config
78
+ 5. **Database creation**: Creates database in running Postgres (idempotent)
79
+ 6. **Package manager**: Detects bun/pnpm/yarn/npm from lockfiles
80
+ 7. **Dependencies**: Runs install at root (monorepo-aware)
47
81
 
48
82
  ## Behavioral Expectations
49
83
 
50
84
  When user asks about worktrees or parallel development:
51
85
 
52
86
  1. Suggest using `/wt create <branch>` for new worktrees
53
- 2. Explain the naming convention if they seem unfamiliar
54
- 3. Mention the auto-setup features (env copying, dep install)
87
+ 2. Explain the three-layer isolation if they have Docker/database conflicts
88
+ 3. Mention `.worktreeinclude` for customizing which files are copied
55
89
  4. Show the `cd` command output for easy navigation
90
+ 5. Explain that `wt remove` stops Docker containers but preserves databases
56
91
 
57
92
  ## Example Interactions
58
93
 
@@ -60,9 +95,19 @@ When user asks about worktrees or parallel development:
60
95
 
61
96
  **Response**: Use `/wt create feature/auth` to create a parallel workspace. This will:
62
97
  - Create `project--feature-auth/` as sibling directory
63
- - Copy your `.env` files
98
+ - Copy your `.env` files and rewrite DB names with `_feature_auth` suffix
99
+ - Generate Docker port offsets so both worktrees can run simultaneously
64
100
  - Install dependencies
65
101
 
66
- **User**: "Show me my worktrees"
102
+ **User**: "My worktrees are conflicting on port 5432"
103
+
104
+ **Response**: The `.worktreeinclude` file's `[docker]` section handles this. With `auto` enabled, `wt create` generates a `docker-compose.worktree.yml` override that offsets all ports. Each worktree gets a deterministic offset based on the branch name. Run `docker compose config` to verify the merged ports.
67
105
 
68
- **Response**: Use `/wt ls` to list all worktrees for the current repo.
106
+ **User**: "How do I customize which files are copied to worktrees?"
107
+
108
+ **Response**: Edit `.worktreeinclude` in your repo root. It uses gitignore-style patterns:
109
+ ```
110
+ .env*
111
+ config/local.yml
112
+ apps/*/.env*
113
+ ```
@@ -1,7 +0,0 @@
1
- <claude-mem-context>
2
- # Recent Activity
3
-
4
- <!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
5
-
6
- *No recent activity*
7
- </claude-mem-context>
@@ -1,14 +0,0 @@
1
- {
2
- "name": "better-auth",
3
- "version": "0.1.0",
4
- "description": "Better Auth integration with MCP server for comprehensive authentication",
5
- "author": {
6
- "name": "Smicolon",
7
- "email": "dev@smicolon.com",
8
- "url": "https://github.com/smicolon"
9
- },
10
- "homepage": "https://github.com/smicolon/ai-kit",
11
- "repository": "https://github.com/smicolon/ai-kit",
12
- "license": "MIT",
13
- "keywords": ["better-auth", "authentication", "auth", "mcp", "passkeys", "2fa", "social-login"]
14
- }
@@ -1,10 +0,0 @@
1
- {
2
- "name": "dev-loop",
3
- "description": "Autonomous development loops for iterative coding with automatic continuation",
4
- "version": "1.0.0",
5
- "author": {
6
- "name": "Smicolon",
7
- "email": "dev@smicolon.com",
8
- "url": "https://github.com/smicolon"
9
- }
10
- }
@@ -1,14 +0,0 @@
1
- {
2
- "name": "failure-log",
3
- "version": "1.0.0",
4
- "description": "Persistent failure memory system that tracks mistakes and prevents repeating them across sessions",
5
- "author": {
6
- "name": "Smicolon",
7
- "email": "dev@smicolon.com",
8
- "url": "https://github.com/smicolon"
9
- },
10
- "homepage": "https://github.com/smicolon/ai-kit",
11
- "repository": "https://github.com/smicolon/ai-kit",
12
- "license": "MIT",
13
- "keywords": ["failure-log", "memory", "learning", "mistakes", "conventions"]
14
- }
@@ -1,10 +0,0 @@
1
- {
2
- "name": "flutter",
3
- "version": "0.1.0",
4
- "description": "Flutter development plugin with Fastlane automation, store publishing, and architecture patterns",
5
- "author": {
6
- "name": "Smicolon",
7
- "email": "dev@smicolon.com"
8
- },
9
- "keywords": ["flutter", "fastlane", "ios", "android", "app-store", "google-play", "mobile"]
10
- }
@@ -1,19 +0,0 @@
1
- {
2
- "name": "hono",
3
- "version": "0.1.0",
4
- "description": "Hono web framework plugin for Claude Code - scaffolding, conventions, testing, and deployment for Bun and Cloudflare Workers",
5
- "author": {
6
- "name": "Smicolon",
7
- "email": "dev@smicolon.com"
8
- },
9
- "repository": "https://github.com/smicolon/ai-kit",
10
- "license": "MIT",
11
- "keywords": [
12
- "hono",
13
- "bun",
14
- "cloudflare-workers",
15
- "edge",
16
- "typescript",
17
- "api"
18
- ]
19
- }
@@ -1,14 +0,0 @@
1
- {
2
- "name": "tanstack-router",
3
- "version": "0.1.0",
4
- "description": "TanStack SPA development with Router, Query, Form, Table, Virtual and full ecosystem",
5
- "author": {
6
- "name": "Smicolon",
7
- "email": "dev@smicolon.com",
8
- "url": "https://github.com/smicolon"
9
- },
10
- "homepage": "https://github.com/smicolon/ai-kit",
11
- "repository": "https://github.com/smicolon/ai-kit",
12
- "license": "MIT",
13
- "keywords": ["tanstack", "react", "router", "query", "form", "table", "virtual", "bun", "spa"]
14
- }
@@ -1,19 +0,0 @@
1
- {
2
- "name": "worktree",
3
- "version": "0.1.0",
4
- "description": "Git worktree manager for parallel development with automatic env copying and dependency installation",
5
- "author": {
6
- "name": "Smicolon",
7
- "email": "dev@smicolon.com",
8
- "url": "https://github.com/smicolon"
9
- },
10
- "license": "MIT",
11
- "repository": "https://github.com/smicolon/ai-kit",
12
- "keywords": [
13
- "git",
14
- "worktree",
15
- "parallel-development",
16
- "branch-management",
17
- "monorepo"
18
- ]
19
- }