@mindfoldhq/trellis 0.1.0 → 0.1.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.
Files changed (64) hide show
  1. package/README.md +58 -11
  2. package/dist/cli/index.js +1 -0
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/commands/init.d.ts.map +1 -1
  5. package/dist/commands/init.js +28 -3
  6. package/dist/commands/init.js.map +1 -1
  7. package/dist/configurators/opencode.d.ts +24 -0
  8. package/dist/configurators/opencode.d.ts.map +1 -0
  9. package/dist/configurators/opencode.js +73 -0
  10. package/dist/configurators/opencode.js.map +1 -0
  11. package/dist/configurators/workflow.d.ts +2 -0
  12. package/dist/configurators/workflow.d.ts.map +1 -1
  13. package/dist/configurators/workflow.js +30 -1
  14. package/dist/configurators/workflow.js.map +1 -1
  15. package/dist/templates/agents/bodies/check.md +91 -0
  16. package/dist/templates/agents/bodies/debug.md +102 -0
  17. package/dist/templates/agents/{dispatch.txt → bodies/dispatch.md} +17 -12
  18. package/dist/templates/agents/bodies/implement.md +94 -0
  19. package/dist/templates/agents/bodies/research.md +113 -0
  20. package/dist/templates/agents/index.d.ts +22 -15
  21. package/dist/templates/agents/index.d.ts.map +1 -1
  22. package/dist/templates/agents/index.js +125 -48
  23. package/dist/templates/agents/index.js.map +1 -1
  24. package/dist/templates/agents/metadata.d.ts +48 -0
  25. package/dist/templates/agents/metadata.d.ts.map +1 -0
  26. package/dist/templates/agents/metadata.js +101 -0
  27. package/dist/templates/agents/metadata.js.map +1 -0
  28. package/dist/templates/commands/claude/parallel.md.txt +199 -0
  29. package/dist/templates/commands/claude/start.md.txt +120 -55
  30. package/dist/templates/commands/common/onboard-developer.txt +2 -2
  31. package/dist/templates/commands/common/record-agent-flow.txt +1 -1
  32. package/dist/templates/commands/cursor/start.md.txt +92 -29
  33. package/dist/templates/commands/index.d.ts +2 -0
  34. package/dist/templates/commands/index.d.ts.map +1 -1
  35. package/dist/templates/commands/index.js +16 -0
  36. package/dist/templates/commands/index.js.map +1 -1
  37. package/dist/templates/commands/opencode/start.md.txt +127 -0
  38. package/dist/templates/markdown/agent-traces-index.md.txt +8 -9
  39. package/dist/templates/markdown/agents.md.txt +1 -1
  40. package/dist/templates/markdown/init-agent.md.txt +8 -8
  41. package/dist/templates/markdown/workflow.md.txt +6 -6
  42. package/dist/templates/scripts/add-session.sh.txt +14 -14
  43. package/dist/templates/scripts/common/developer.sh.txt +13 -13
  44. package/dist/templates/scripts/common/git-context.sh.txt +8 -8
  45. package/dist/templates/scripts/common/paths.sh.txt +4 -4
  46. package/dist/templates/scripts/common/worktree.sh.txt +138 -0
  47. package/dist/templates/scripts/feature.sh.txt +292 -0
  48. package/dist/templates/scripts/index.d.ts +12 -1
  49. package/dist/templates/scripts/index.d.ts.map +1 -1
  50. package/dist/templates/scripts/index.js +14 -1
  51. package/dist/templates/scripts/index.js.map +1 -1
  52. package/dist/templates/scripts/multi-agent/cleanup.sh.txt +327 -0
  53. package/dist/templates/scripts/multi-agent/start.sh.txt +323 -0
  54. package/dist/templates/scripts/multi-agent/status.sh.txt +423 -0
  55. package/dist/templates/scripts/worktree.yaml.txt +49 -0
  56. package/dist/types/ai-tools.d.ts +2 -2
  57. package/dist/types/ai-tools.d.ts.map +1 -1
  58. package/dist/types/ai-tools.js +4 -0
  59. package/dist/types/ai-tools.js.map +1 -1
  60. package/package.json +1 -1
  61. package/dist/templates/agents/check.txt +0 -120
  62. package/dist/templates/agents/debug.txt +0 -121
  63. package/dist/templates/agents/implement.txt +0 -114
  64. package/dist/templates/agents/research.txt +0 -258
@@ -0,0 +1,327 @@
1
+ #!/bin/bash
2
+ # =============================================================================
3
+ # Multi-Agent Pipeline: Cleanup Worktree
4
+ # =============================================================================
5
+ # Usage:
6
+ # ./cleanup.sh <branch-name> Remove specific worktree
7
+ # ./cleanup.sh --list List all worktrees
8
+ # ./cleanup.sh --merged Remove merged worktrees
9
+ # ./cleanup.sh --all Remove all worktrees (with confirmation)
10
+ #
11
+ # Options:
12
+ # -y, --yes Skip confirmation prompts
13
+ # --keep-branch Don't delete the git branch
14
+ #
15
+ # This script:
16
+ # 1. Archives feature directory to archive/{YYYY-MM}/
17
+ # 2. Removes agent from registry
18
+ # 3. Removes git worktree
19
+ # 4. Optionally deletes git branch
20
+ # =============================================================================
21
+
22
+ set -e
23
+
24
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
25
+ source "$SCRIPT_DIR/../common/paths.sh"
26
+ source "$SCRIPT_DIR/../common/worktree.sh"
27
+ source "$SCRIPT_DIR/../common/developer.sh"
28
+
29
+ # Colors
30
+ RED='\033[0;31m'
31
+ GREEN='\033[0;32m'
32
+ YELLOW='\033[1;33m'
33
+ BLUE='\033[0;34m'
34
+ CYAN='\033[0;36m'
35
+ NC='\033[0m'
36
+
37
+ log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
38
+ log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
39
+ log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
40
+ log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
41
+
42
+ PROJECT_ROOT=$(get_repo_root)
43
+ SKIP_CONFIRM=false
44
+ KEEP_BRANCH=false
45
+
46
+ # =============================================================================
47
+ # Parse Arguments
48
+ # =============================================================================
49
+ POSITIONAL_ARGS=()
50
+ while [[ $# -gt 0 ]]; do
51
+ case $1 in
52
+ -y|--yes)
53
+ SKIP_CONFIRM=true
54
+ shift
55
+ ;;
56
+ --keep-branch)
57
+ KEEP_BRANCH=true
58
+ shift
59
+ ;;
60
+ --list|--merged|--all)
61
+ ACTION="${1#--}"
62
+ shift
63
+ ;;
64
+ -*)
65
+ log_error "Unknown option: $1"
66
+ exit 1
67
+ ;;
68
+ *)
69
+ POSITIONAL_ARGS+=("$1")
70
+ shift
71
+ ;;
72
+ esac
73
+ done
74
+
75
+ # =============================================================================
76
+ # List Worktrees
77
+ # =============================================================================
78
+ cmd_list() {
79
+ echo -e "${BLUE}=== Git Worktrees ===${NC}"
80
+ echo ""
81
+
82
+ cd "$PROJECT_ROOT"
83
+ git worktree list
84
+
85
+ echo ""
86
+
87
+ # Show registry info
88
+ AGENTS_DIR=$(get_agents_dir)
89
+ REGISTRY_FILE="${AGENTS_DIR}/registry.json"
90
+
91
+ if [ -f "$REGISTRY_FILE" ]; then
92
+ echo -e "${BLUE}=== Registered Agents ===${NC}"
93
+ echo ""
94
+ jq -r '.agents[] | " \(.id): PID=\(.pid) [\(.worktree_path)]"' "$REGISTRY_FILE" 2>/dev/null || echo " (none)"
95
+ echo ""
96
+ fi
97
+ }
98
+
99
+ # =============================================================================
100
+ # Archive Feature
101
+ # =============================================================================
102
+ archive_feature() {
103
+ local worktree_path="$1"
104
+
105
+ # Find feature directory from registry
106
+ AGENTS_DIR=$(get_agents_dir)
107
+ REGISTRY_FILE="${AGENTS_DIR}/registry.json"
108
+
109
+ if [ ! -f "$REGISTRY_FILE" ]; then
110
+ return 0
111
+ fi
112
+
113
+ FEATURE_DIR=$(jq -r --arg path "$worktree_path" '.agents[] | select(.worktree_path == $path) | .feature_dir' "$REGISTRY_FILE" 2>/dev/null)
114
+
115
+ if [ -z "$FEATURE_DIR" ] || [ "$FEATURE_DIR" = "null" ]; then
116
+ return 0
117
+ fi
118
+
119
+ FEATURE_DIR_ABS="${PROJECT_ROOT}/${FEATURE_DIR}"
120
+ if [ ! -d "$FEATURE_DIR_ABS" ]; then
121
+ return 0
122
+ fi
123
+
124
+ # Archive to archive/{YYYY-MM}/
125
+ local features_dir=$(get_features_dir)
126
+ local archive_dir="$features_dir/archive"
127
+ local year_month=$(date +%Y-%m)
128
+ local month_dir="$archive_dir/$year_month"
129
+
130
+ mkdir -p "$month_dir"
131
+
132
+ local feature_name=$(basename "$FEATURE_DIR")
133
+ mv "$FEATURE_DIR_ABS" "$month_dir/"
134
+
135
+ log_success "Archived feature: $feature_name -> archive/$year_month/"
136
+ }
137
+
138
+ # =============================================================================
139
+ # Remove from Registry
140
+ # =============================================================================
141
+ remove_from_registry() {
142
+ local worktree_path="$1"
143
+
144
+ AGENTS_DIR=$(get_agents_dir)
145
+ REGISTRY_FILE="${AGENTS_DIR}/registry.json"
146
+
147
+ if [ ! -f "$REGISTRY_FILE" ]; then
148
+ return 0
149
+ fi
150
+
151
+ # Remove by worktree path
152
+ local updated=$(jq --arg path "$worktree_path" '.agents = [.agents[] | select(.worktree_path != $path)]' "$REGISTRY_FILE")
153
+ echo "$updated" | jq '.' > "$REGISTRY_FILE"
154
+
155
+ log_info "Removed from registry"
156
+ }
157
+
158
+ # =============================================================================
159
+ # Cleanup Single Worktree
160
+ # =============================================================================
161
+ cleanup_worktree() {
162
+ local branch="$1"
163
+
164
+ cd "$PROJECT_ROOT"
165
+
166
+ # Find worktree path for branch
167
+ # porcelain format: worktree line comes BEFORE branch line, so use -B2
168
+ local worktree_info=$(git worktree list --porcelain | grep -B2 "branch refs/heads/$branch" | head -3)
169
+ local worktree_path=$(echo "$worktree_info" | grep "^worktree " | cut -d' ' -f2-)
170
+
171
+ if [ -z "$worktree_path" ]; then
172
+ log_error "No worktree found for branch: $branch"
173
+ exit 1
174
+ fi
175
+
176
+ echo ""
177
+ echo -e "${BLUE}=== Cleanup Worktree ===${NC}"
178
+ echo " Branch: $branch"
179
+ echo " Worktree: $worktree_path"
180
+ echo ""
181
+
182
+ # Confirmation
183
+ if [ "$SKIP_CONFIRM" != "true" ]; then
184
+ read -p "Remove this worktree? [y/N] " -n 1 -r
185
+ echo
186
+ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
187
+ log_info "Aborted"
188
+ exit 0
189
+ fi
190
+ fi
191
+
192
+ # 1. Archive feature
193
+ archive_feature "$worktree_path"
194
+
195
+ # 2. Remove from registry
196
+ remove_from_registry "$worktree_path"
197
+
198
+ # 3. Remove worktree
199
+ log_info "Removing worktree..."
200
+ git worktree remove "$worktree_path" --force 2>/dev/null || rm -rf "$worktree_path"
201
+ log_success "Worktree removed"
202
+
203
+ # 4. Delete branch (optional)
204
+ if [ "$KEEP_BRANCH" != "true" ]; then
205
+ log_info "Deleting branch..."
206
+ git branch -D "$branch" 2>/dev/null || log_warn "Could not delete branch (may be checked out elsewhere)"
207
+ fi
208
+
209
+ log_success "Cleanup complete for: $branch"
210
+ }
211
+
212
+ # =============================================================================
213
+ # Cleanup Merged Worktrees
214
+ # =============================================================================
215
+ cmd_merged() {
216
+ cd "$PROJECT_ROOT"
217
+
218
+ local main_branch=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@' || echo "main")
219
+
220
+ echo -e "${BLUE}=== Finding Merged Worktrees ===${NC}"
221
+ echo ""
222
+
223
+ local merged_branches=$(git branch --merged "$main_branch" | grep -v "^\*" | grep -v "$main_branch" | tr -d ' ')
224
+
225
+ if [ -z "$merged_branches" ]; then
226
+ log_info "No merged branches found"
227
+ exit 0
228
+ fi
229
+
230
+ local worktree_branches=""
231
+ while IFS= read -r branch; do
232
+ if git worktree list | grep -q "\[$branch\]"; then
233
+ worktree_branches="$worktree_branches $branch"
234
+ echo " - $branch"
235
+ fi
236
+ done <<< "$merged_branches"
237
+
238
+ if [ -z "$worktree_branches" ]; then
239
+ log_info "No merged worktrees found"
240
+ exit 0
241
+ fi
242
+
243
+ echo ""
244
+
245
+ if [ "$SKIP_CONFIRM" != "true" ]; then
246
+ read -p "Remove these merged worktrees? [y/N] " -n 1 -r
247
+ echo
248
+ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
249
+ log_info "Aborted"
250
+ exit 0
251
+ fi
252
+ fi
253
+
254
+ for branch in $worktree_branches; do
255
+ cleanup_worktree "$branch"
256
+ done
257
+ }
258
+
259
+ # =============================================================================
260
+ # Cleanup All Worktrees
261
+ # =============================================================================
262
+ cmd_all() {
263
+ cd "$PROJECT_ROOT"
264
+
265
+ echo -e "${BLUE}=== All Worktrees ===${NC}"
266
+ echo ""
267
+
268
+ local worktrees=$(git worktree list --porcelain | grep "^worktree " | grep -v "$PROJECT_ROOT$" | cut -d' ' -f2-)
269
+
270
+ if [ -z "$worktrees" ]; then
271
+ log_info "No worktrees to remove"
272
+ exit 0
273
+ fi
274
+
275
+ while IFS= read -r wt; do
276
+ echo " - $wt"
277
+ done <<< "$worktrees"
278
+
279
+ echo ""
280
+
281
+ if [ "$SKIP_CONFIRM" != "true" ]; then
282
+ echo -e "${RED}WARNING: This will remove ALL worktrees!${NC}"
283
+ read -p "Are you sure? [y/N] " -n 1 -r
284
+ echo
285
+ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
286
+ log_info "Aborted"
287
+ exit 0
288
+ fi
289
+ fi
290
+
291
+ while IFS= read -r wt; do
292
+ local branch=$(git worktree list | grep "$wt" | awk '{print $NF}' | tr -d '[]')
293
+ if [ -n "$branch" ]; then
294
+ cleanup_worktree "$branch"
295
+ fi
296
+ done <<< "$worktrees"
297
+ }
298
+
299
+ # =============================================================================
300
+ # Main
301
+ # =============================================================================
302
+ case "${ACTION:-}" in
303
+ list)
304
+ cmd_list
305
+ ;;
306
+ merged)
307
+ cmd_merged
308
+ ;;
309
+ all)
310
+ cmd_all
311
+ ;;
312
+ *)
313
+ if [ ${#POSITIONAL_ARGS[@]} -eq 0 ]; then
314
+ echo "Usage:"
315
+ echo " $0 <branch-name> Remove specific worktree"
316
+ echo " $0 --list List all worktrees"
317
+ echo " $0 --merged Remove merged worktrees"
318
+ echo " $0 --all Remove all worktrees"
319
+ echo ""
320
+ echo "Options:"
321
+ echo " -y, --yes Skip confirmation"
322
+ echo " --keep-branch Don't delete git branch"
323
+ exit 1
324
+ fi
325
+ cleanup_worktree "${POSITIONAL_ARGS[0]}"
326
+ ;;
327
+ esac
@@ -0,0 +1,323 @@
1
+ #!/bin/bash
2
+ # =============================================================================
3
+ # Multi-Agent Pipeline: Start Worktree Agent
4
+ # =============================================================================
5
+ # Usage: ./start.sh <feature-dir>
6
+ # Example: ./start.sh .trellis/agent-traces/taosu/features/16-my-feature
7
+ #
8
+ # This script:
9
+ # 1. Creates worktree (if not exists) with dependency install
10
+ # 2. Copies environment files (from worktree.yaml config)
11
+ # 3. Sets .current-feature in worktree
12
+ # 4. Starts claude agent in background
13
+ # 5. Registers agent to registry.json
14
+ #
15
+ # Prerequisites:
16
+ # - feature.json must exist with 'branch' field
17
+ # - .claude/agents/dispatch.md must exist
18
+ #
19
+ # Configuration: .trellis/worktree.yaml
20
+ # =============================================================================
21
+
22
+ set -e
23
+
24
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
25
+ source "$SCRIPT_DIR/../common/paths.sh"
26
+ source "$SCRIPT_DIR/../common/worktree.sh"
27
+ source "$SCRIPT_DIR/../common/developer.sh"
28
+
29
+ # Colors
30
+ RED='\033[0;31m'
31
+ GREEN='\033[0;32m'
32
+ YELLOW='\033[1;33m'
33
+ BLUE='\033[0;34m'
34
+ NC='\033[0m'
35
+
36
+ log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
37
+ log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
38
+ log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
39
+ log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
40
+
41
+ # =============================================================================
42
+ # Constants
43
+ # =============================================================================
44
+ PROJECT_ROOT=$(get_repo_root)
45
+ DISPATCH_MD_PATH=".claude/agents/dispatch.md"
46
+
47
+ # =============================================================================
48
+ # Parse Arguments
49
+ # =============================================================================
50
+ FEATURE_DIR=$1
51
+ if [ -z "$FEATURE_DIR" ]; then
52
+ log_error "Feature directory required"
53
+ echo "Usage: $0 <feature-dir>"
54
+ echo "Example: $0 .trellis/agent-traces/taosu/features/16-my-feature"
55
+ exit 1
56
+ fi
57
+
58
+ # Normalize paths
59
+ if [[ "$FEATURE_DIR" = /* ]]; then
60
+ FEATURE_DIR_RELATIVE="${FEATURE_DIR#$PROJECT_ROOT/}"
61
+ FEATURE_DIR_ABS="$FEATURE_DIR"
62
+ else
63
+ FEATURE_DIR_RELATIVE="$FEATURE_DIR"
64
+ FEATURE_DIR_ABS="${PROJECT_ROOT}/${FEATURE_DIR}"
65
+ fi
66
+
67
+ FEATURE_JSON="${FEATURE_DIR_ABS}/feature.json"
68
+
69
+ # =============================================================================
70
+ # Validation
71
+ # =============================================================================
72
+ if [ ! -f "$FEATURE_JSON" ]; then
73
+ log_error "feature.json not found at ${FEATURE_JSON}"
74
+ exit 1
75
+ fi
76
+
77
+ DISPATCH_MD="${PROJECT_ROOT}/${DISPATCH_MD_PATH}"
78
+ if [ ! -f "$DISPATCH_MD" ]; then
79
+ log_error "dispatch.md not found at ${DISPATCH_MD}"
80
+ exit 1
81
+ fi
82
+
83
+ CONFIG_FILE=$(get_worktree_config "$PROJECT_ROOT")
84
+ if [ ! -f "$CONFIG_FILE" ]; then
85
+ log_error "worktree.yaml not found at ${CONFIG_FILE}"
86
+ exit 1
87
+ fi
88
+
89
+ # =============================================================================
90
+ # Read Feature Config
91
+ # =============================================================================
92
+ echo ""
93
+ echo -e "${BLUE}=== Multi-Agent Pipeline: Start ===${NC}"
94
+ log_info "Feature: ${FEATURE_DIR_ABS}"
95
+
96
+ BRANCH=$(jq -r '.branch' "$FEATURE_JSON")
97
+ FEATURE_NAME=$(jq -r '.name' "$FEATURE_JSON")
98
+ WORKTREE_PATH=$(jq -r '.worktree_path // empty' "$FEATURE_JSON")
99
+
100
+ if [ -z "$BRANCH" ] || [ "$BRANCH" = "null" ]; then
101
+ log_error "branch field not set in feature.json"
102
+ log_info "Please set branch field first, e.g.:"
103
+ log_info " jq '.branch = \"feature/my-feature\"' feature.json > tmp && mv tmp feature.json"
104
+ exit 1
105
+ fi
106
+
107
+ log_info "Branch: ${BRANCH}"
108
+ log_info "Name: ${FEATURE_NAME}"
109
+
110
+ # =============================================================================
111
+ # Step 1: Create Worktree (if not exists)
112
+ # =============================================================================
113
+ if [ -z "$WORKTREE_PATH" ] || [ ! -d "$WORKTREE_PATH" ]; then
114
+ log_info "Step 1: Creating worktree..."
115
+
116
+ # Record current branch as base_branch (PR target)
117
+ BASE_BRANCH=$(git -C "$PROJECT_ROOT" branch --show-current)
118
+ log_info "Base branch (PR target): ${BASE_BRANCH}"
119
+
120
+ # Calculate worktree path
121
+ WORKTREE_BASE=$(get_worktree_base_dir "$PROJECT_ROOT")
122
+ mkdir -p "$WORKTREE_BASE"
123
+ WORKTREE_BASE="$(cd "$WORKTREE_BASE" && pwd)"
124
+ WORKTREE_PATH="${WORKTREE_BASE}/${BRANCH}"
125
+
126
+ # Create parent directory
127
+ mkdir -p "$(dirname "$WORKTREE_PATH")"
128
+ cd "$PROJECT_ROOT"
129
+
130
+ # Create branch if not exists
131
+ if git show-ref --verify --quiet "refs/heads/${BRANCH}"; then
132
+ log_info "Branch exists, checking out..."
133
+ git worktree add "$WORKTREE_PATH" "$BRANCH"
134
+ else
135
+ log_info "Creating new branch: $BRANCH"
136
+ git worktree add -b "$BRANCH" "$WORKTREE_PATH"
137
+ fi
138
+
139
+ log_success "Worktree created: ${WORKTREE_PATH}"
140
+
141
+ # Update feature.json with worktree_path and base_branch
142
+ jq --arg path "$WORKTREE_PATH" --arg base "$BASE_BRANCH" \
143
+ '.worktree_path = $path | .base_branch = $base' "$FEATURE_JSON" > "${FEATURE_JSON}.tmp"
144
+ mv "${FEATURE_JSON}.tmp" "$FEATURE_JSON"
145
+
146
+ # ----- Copy environment files -----
147
+ log_info "Copying environment files..."
148
+ cd "$WORKTREE_PATH"
149
+
150
+ COPY_LIST=$(get_worktree_copy_files "$PROJECT_ROOT")
151
+ COPY_COUNT=0
152
+
153
+ while IFS= read -r item; do
154
+ [ -z "$item" ] && continue
155
+
156
+ SOURCE="${PROJECT_ROOT}/${item}"
157
+ TARGET="${WORKTREE_PATH}/${item}"
158
+
159
+ if [ -f "$SOURCE" ]; then
160
+ mkdir -p "$(dirname "$TARGET")"
161
+ cp "$SOURCE" "$TARGET"
162
+ ((COPY_COUNT++))
163
+ fi
164
+ done <<< "$COPY_LIST"
165
+
166
+ if [ $COPY_COUNT -gt 0 ]; then
167
+ log_success "Copied $COPY_COUNT file(s)"
168
+ fi
169
+
170
+ # ----- Copy feature directory (may not be committed yet) -----
171
+ log_info "Copying feature directory..."
172
+ FEATURE_TARGET_DIR="${WORKTREE_PATH}/${FEATURE_DIR_RELATIVE}"
173
+ mkdir -p "$(dirname "$FEATURE_TARGET_DIR")"
174
+ cp -r "$FEATURE_DIR_ABS" "$(dirname "$FEATURE_TARGET_DIR")/"
175
+ log_success "Feature directory copied to worktree"
176
+
177
+ # ----- Run post_create hooks -----
178
+ log_info "Running post_create hooks..."
179
+
180
+ POST_CREATE=$(get_worktree_post_create_hooks "$PROJECT_ROOT")
181
+ HOOK_COUNT=0
182
+
183
+ while IFS= read -r cmd; do
184
+ [ -z "$cmd" ] && continue
185
+
186
+ log_info " Running: $cmd"
187
+ if eval "$cmd"; then
188
+ ((HOOK_COUNT++))
189
+ else
190
+ log_error "Hook failed: $cmd"
191
+ exit 1
192
+ fi
193
+ done <<< "$POST_CREATE"
194
+
195
+ if [ $HOOK_COUNT -gt 0 ]; then
196
+ log_success "Ran $HOOK_COUNT hook(s)"
197
+ fi
198
+
199
+ else
200
+ log_info "Step 1: Using existing worktree: ${WORKTREE_PATH}"
201
+ fi
202
+
203
+ # =============================================================================
204
+ # Step 2: Set .current-feature in Worktree
205
+ # =============================================================================
206
+ log_info "Step 2: Setting current feature in worktree..."
207
+
208
+ mkdir -p "${WORKTREE_PATH}/$DIR_WORKFLOW"
209
+ echo "$FEATURE_DIR_RELATIVE" > "${WORKTREE_PATH}/$DIR_WORKFLOW/$FILE_CURRENT_FEATURE"
210
+ log_success "Current feature set: ${FEATURE_DIR_RELATIVE}"
211
+
212
+ # =============================================================================
213
+ # Step 3: Prepare and Start Claude Agent
214
+ # =============================================================================
215
+ log_info "Step 3: Starting Claude agent..."
216
+
217
+ # Extract dispatch.md content (skip frontmatter)
218
+ DISPATCH_PROMPT=$(awk '
219
+ BEGIN { in_frontmatter = 0; started = 0 }
220
+ /^---$/ {
221
+ if (!started) { in_frontmatter = 1; started = 1; next }
222
+ else if (in_frontmatter) { in_frontmatter = 0; next }
223
+ }
224
+ !in_frontmatter { print }
225
+ ' "$DISPATCH_MD")
226
+
227
+ # Update feature status
228
+ jq '.status = "in_progress"' "$FEATURE_JSON" > "${FEATURE_JSON}.tmp"
229
+ mv "${FEATURE_JSON}.tmp" "$FEATURE_JSON"
230
+
231
+ cd "$WORKTREE_PATH"
232
+
233
+ LOG_FILE="${WORKTREE_PATH}/.agent-log"
234
+ PROMPT_FILE="${WORKTREE_PATH}/.agent-prompt"
235
+ RUNNER_SCRIPT="${WORKTREE_PATH}/.agent-runner.sh"
236
+
237
+ touch "$LOG_FILE"
238
+ echo "$DISPATCH_PROMPT" > "$PROMPT_FILE"
239
+
240
+ # Create runner script
241
+ cat > "$RUNNER_SCRIPT" << 'RUNNER_EOF'
242
+ #!/bin/bash
243
+ cd "$(dirname "$0")"
244
+ export https_proxy="${AGENT_HTTPS_PROXY:-}"
245
+ export http_proxy="${AGENT_HTTP_PROXY:-}"
246
+ export all_proxy="${AGENT_ALL_PROXY:-}"
247
+
248
+ claude -p --dangerously-skip-permissions --output-format stream-json --verbose < .agent-prompt
249
+ RUNNER_EOF
250
+ chmod +x "$RUNNER_SCRIPT"
251
+
252
+ # Start agent in background
253
+ AGENT_HTTPS_PROXY="${https_proxy:-}" \
254
+ AGENT_HTTP_PROXY="${http_proxy:-}" \
255
+ AGENT_ALL_PROXY="${all_proxy:-}" \
256
+ nohup "$RUNNER_SCRIPT" > "$LOG_FILE" 2>&1 &
257
+ AGENT_PID=$!
258
+
259
+ log_success "Agent started with PID: ${AGENT_PID}"
260
+
261
+ # =============================================================================
262
+ # Step 4: Register to Registry (in main repo, not worktree)
263
+ # =============================================================================
264
+ log_info "Step 4: Registering agent to registry..."
265
+
266
+ DEVELOPER=$(get_developer "$PROJECT_ROOT")
267
+ AGENTS_DIR=$(get_agents_dir "$PROJECT_ROOT")
268
+ mkdir -p "$AGENTS_DIR"
269
+
270
+ REGISTRY_FILE="${AGENTS_DIR}/registry.json"
271
+
272
+ # Generate agent ID
273
+ FEATURE_ID=$(jq -r '.id // empty' "$FEATURE_JSON")
274
+ if [ -z "$FEATURE_ID" ]; then
275
+ FEATURE_ID=$(echo "$BRANCH" | sed 's/\//-/g')
276
+ fi
277
+
278
+ # Read or create registry
279
+ if [ -f "$REGISTRY_FILE" ]; then
280
+ REGISTRY=$(cat "$REGISTRY_FILE")
281
+ else
282
+ REGISTRY='{"agents":[]}'
283
+ fi
284
+
285
+ # Remove old record with same ID
286
+ REGISTRY=$(echo "$REGISTRY" | jq --arg id "$FEATURE_ID" '.agents = [.agents[] | select(.id != $id)]')
287
+
288
+ # Add new agent record
289
+ STARTED_AT=$(date -Iseconds)
290
+ NEW_AGENT=$(jq -n \
291
+ --arg id "$FEATURE_ID" \
292
+ --arg worktree "$WORKTREE_PATH" \
293
+ --arg pid "$AGENT_PID" \
294
+ --arg started_at "$STARTED_AT" \
295
+ --arg feature_dir "$FEATURE_DIR_RELATIVE" \
296
+ '{
297
+ id: $id,
298
+ worktree_path: $worktree,
299
+ pid: ($pid | tonumber),
300
+ started_at: $started_at,
301
+ feature_dir: $feature_dir
302
+ }')
303
+
304
+ REGISTRY=$(echo "$REGISTRY" | jq --argjson agent "$NEW_AGENT" '.agents += [$agent]')
305
+ echo "$REGISTRY" | jq '.' > "$REGISTRY_FILE"
306
+
307
+ log_success "Agent registered: ${FEATURE_ID}"
308
+
309
+ # =============================================================================
310
+ # Summary
311
+ # =============================================================================
312
+ echo ""
313
+ echo -e "${GREEN}=== Agent Started ===${NC}"
314
+ echo ""
315
+ echo " ID: $FEATURE_ID"
316
+ echo " PID: $AGENT_PID"
317
+ echo " Worktree: $WORKTREE_PATH"
318
+ echo " Feature: $FEATURE_DIR_RELATIVE"
319
+ echo " Log: $LOG_FILE"
320
+ echo " Registry: $REGISTRY_FILE"
321
+ echo ""
322
+ echo -e "${YELLOW}To monitor:${NC} tail -f $LOG_FILE"
323
+ echo -e "${YELLOW}To stop:${NC} kill $AGENT_PID"