@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.
- package/README.md +58 -11
- package/dist/cli/index.js +1 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +28 -3
- package/dist/commands/init.js.map +1 -1
- package/dist/configurators/opencode.d.ts +24 -0
- package/dist/configurators/opencode.d.ts.map +1 -0
- package/dist/configurators/opencode.js +73 -0
- package/dist/configurators/opencode.js.map +1 -0
- package/dist/configurators/workflow.d.ts +2 -0
- package/dist/configurators/workflow.d.ts.map +1 -1
- package/dist/configurators/workflow.js +30 -1
- package/dist/configurators/workflow.js.map +1 -1
- package/dist/templates/agents/bodies/check.md +91 -0
- package/dist/templates/agents/bodies/debug.md +102 -0
- package/dist/templates/agents/{dispatch.txt → bodies/dispatch.md} +17 -12
- package/dist/templates/agents/bodies/implement.md +94 -0
- package/dist/templates/agents/bodies/research.md +113 -0
- package/dist/templates/agents/index.d.ts +22 -15
- package/dist/templates/agents/index.d.ts.map +1 -1
- package/dist/templates/agents/index.js +125 -48
- package/dist/templates/agents/index.js.map +1 -1
- package/dist/templates/agents/metadata.d.ts +48 -0
- package/dist/templates/agents/metadata.d.ts.map +1 -0
- package/dist/templates/agents/metadata.js +101 -0
- package/dist/templates/agents/metadata.js.map +1 -0
- package/dist/templates/commands/claude/parallel.md.txt +199 -0
- package/dist/templates/commands/claude/start.md.txt +120 -55
- package/dist/templates/commands/common/onboard-developer.txt +2 -2
- package/dist/templates/commands/common/record-agent-flow.txt +1 -1
- package/dist/templates/commands/cursor/start.md.txt +92 -29
- package/dist/templates/commands/index.d.ts +2 -0
- package/dist/templates/commands/index.d.ts.map +1 -1
- package/dist/templates/commands/index.js +16 -0
- package/dist/templates/commands/index.js.map +1 -1
- package/dist/templates/commands/opencode/start.md.txt +127 -0
- package/dist/templates/markdown/agent-traces-index.md.txt +8 -9
- package/dist/templates/markdown/agents.md.txt +1 -1
- package/dist/templates/markdown/init-agent.md.txt +8 -8
- package/dist/templates/markdown/workflow.md.txt +6 -6
- package/dist/templates/scripts/add-session.sh.txt +14 -14
- package/dist/templates/scripts/common/developer.sh.txt +13 -13
- package/dist/templates/scripts/common/git-context.sh.txt +8 -8
- package/dist/templates/scripts/common/paths.sh.txt +4 -4
- package/dist/templates/scripts/common/worktree.sh.txt +138 -0
- package/dist/templates/scripts/feature.sh.txt +292 -0
- package/dist/templates/scripts/index.d.ts +12 -1
- package/dist/templates/scripts/index.d.ts.map +1 -1
- package/dist/templates/scripts/index.js +14 -1
- package/dist/templates/scripts/index.js.map +1 -1
- package/dist/templates/scripts/multi-agent/cleanup.sh.txt +327 -0
- package/dist/templates/scripts/multi-agent/start.sh.txt +323 -0
- package/dist/templates/scripts/multi-agent/status.sh.txt +423 -0
- package/dist/templates/scripts/worktree.yaml.txt +49 -0
- package/dist/types/ai-tools.d.ts +2 -2
- package/dist/types/ai-tools.d.ts.map +1 -1
- package/dist/types/ai-tools.js +4 -0
- package/dist/types/ai-tools.js.map +1 -1
- package/package.json +1 -1
- package/dist/templates/agents/check.txt +0 -120
- package/dist/templates/agents/debug.txt +0 -121
- package/dist/templates/agents/implement.txt +0 -114
- 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"
|