@mindfoldhq/trellis 0.1.1 → 0.1.3
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/dist/{templates/agents/bodies → .claude/agents}/check.md +7 -0
- package/dist/{templates/agents/bodies → .claude/agents}/debug.md +7 -0
- package/dist/{templates/agents/bodies → .claude/agents}/dispatch.md +11 -8
- package/dist/{templates/agents/bodies → .claude/agents}/implement.md +7 -0
- package/dist/.claude/agents/plan.md +396 -0
- package/dist/{templates/agents/bodies → .claude/agents}/research.md +7 -0
- package/dist/{templates/commands/common/check-cross-layer.txt → .claude/commands/check-cross-layer.md} +29 -29
- package/dist/{templates → .claude}/hooks/inject-subagent-context.py +63 -0
- package/dist/.cursor/commands/before-backend-dev.md +13 -0
- package/dist/.cursor/commands/before-frontend-dev.md +13 -0
- package/dist/.cursor/commands/break-loop.md +107 -0
- package/dist/.cursor/commands/check-backend.md +13 -0
- package/dist/.cursor/commands/check-cross-layer.md +153 -0
- package/dist/.cursor/commands/check-frontend.md +13 -0
- package/dist/.cursor/commands/create-command.md +154 -0
- package/dist/.cursor/commands/finish-work.md +129 -0
- package/dist/.cursor/commands/integrate-skill.md +219 -0
- package/dist/.cursor/commands/onboard-developer.md +355 -0
- package/dist/.cursor/commands/record-agent-flow.md +62 -0
- package/dist/.trellis/scripts/common/phase.sh +150 -0
- package/dist/{templates/scripts/feature.sh.txt → .trellis/scripts/feature.sh} +8 -3
- package/dist/{templates/scripts/multi-agent/cleanup.sh.txt → .trellis/scripts/multi-agent/cleanup.sh} +107 -18
- package/dist/.trellis/scripts/multi-agent/create-pr.sh +241 -0
- package/dist/.trellis/scripts/multi-agent/plan.sh +232 -0
- package/dist/{templates/scripts/multi-agent/start.sh.txt → .trellis/scripts/multi-agent/start.sh} +21 -0
- package/dist/{templates/scripts/multi-agent/status.sh.txt → .trellis/scripts/multi-agent/status.sh} +282 -10
- package/dist/.trellis/structure/backend/database-guidelines.md +51 -0
- package/dist/.trellis/structure/backend/directory-structure.md +209 -0
- package/dist/.trellis/structure/backend/error-handling.md +278 -0
- package/dist/.trellis/structure/backend/index.md +38 -0
- package/dist/.trellis/structure/backend/logging-guidelines.md +266 -0
- package/dist/.trellis/structure/backend/quality-guidelines.md +313 -0
- package/dist/.trellis/structure/frontend/component-guidelines.md +59 -0
- package/dist/.trellis/structure/frontend/directory-structure.md +54 -0
- package/dist/.trellis/structure/frontend/hook-guidelines.md +51 -0
- package/dist/.trellis/structure/frontend/index.md +39 -0
- package/dist/.trellis/structure/frontend/quality-guidelines.md +51 -0
- package/dist/.trellis/structure/frontend/state-management.md +51 -0
- package/dist/.trellis/structure/frontend/type-safety.md +51 -0
- package/dist/.trellis/structure/guides/code-reuse-thinking-guide.md +92 -0
- package/dist/.trellis/structure/guides/cross-layer-thinking-guide.md +94 -0
- package/dist/.trellis/structure/guides/index.md +79 -0
- package/dist/{templates/scripts/worktree.yaml.txt → .trellis/worktree.yaml} +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +5 -16
- package/dist/commands/init.js.map +1 -1
- package/dist/configurators/claude.d.ts +17 -17
- package/dist/configurators/claude.d.ts.map +1 -1
- package/dist/configurators/claude.js +29 -59
- package/dist/configurators/claude.js.map +1 -1
- package/dist/configurators/cursor.d.ts +3 -3
- package/dist/configurators/cursor.d.ts.map +1 -1
- package/dist/configurators/cursor.js +11 -15
- package/dist/configurators/cursor.js.map +1 -1
- package/dist/configurators/opencode.d.ts +11 -10
- package/dist/configurators/opencode.d.ts.map +1 -1
- package/dist/configurators/opencode.js +14 -33
- package/dist/configurators/opencode.js.map +1 -1
- package/dist/configurators/workflow.d.ts +7 -0
- package/dist/configurators/workflow.d.ts.map +1 -1
- package/dist/configurators/workflow.js +30 -99
- package/dist/configurators/workflow.js.map +1 -1
- package/dist/templates/extract.d.ts +73 -8
- package/dist/templates/extract.d.ts.map +1 -1
- package/dist/templates/extract.js +149 -10
- package/dist/templates/extract.js.map +1 -1
- package/dist/templates/markdown/gitignore.txt +6 -1
- package/dist/templates/markdown/index.d.ts +5 -1
- package/dist/templates/markdown/index.d.ts.map +1 -1
- package/dist/templates/markdown/index.js +54 -26
- package/dist/templates/markdown/index.js.map +1 -1
- package/dist/templates/markdown/structure/backend/directory-structure.md.txt +6 -6
- package/dist/templates/markdown/structure/backend/error-handling.md.txt +8 -8
- package/dist/templates/markdown/structure/backend/index.md.txt +5 -5
- package/dist/templates/markdown/structure/backend/logging-guidelines.md.txt +8 -8
- package/dist/templates/markdown/structure/frontend/index.md.txt +6 -6
- package/dist/templates/markdown/structure/guides/cross-layer-thinking-guide.md.txt +5 -5
- package/dist/templates/markdown/structure/guides/index.md.txt +7 -7
- package/dist/templates/markdown/worktree.yaml.txt +58 -0
- package/package.json +1 -1
- package/dist/configurators/templates.d.ts +0 -40
- package/dist/configurators/templates.d.ts.map +0 -1
- package/dist/configurators/templates.js +0 -67
- package/dist/configurators/templates.js.map +0 -1
- package/dist/templates/agents/index.d.ts +0 -42
- package/dist/templates/agents/index.d.ts.map +0 -1
- package/dist/templates/agents/index.js +0 -148
- package/dist/templates/agents/index.js.map +0 -1
- package/dist/templates/agents/metadata.d.ts +0 -48
- package/dist/templates/agents/metadata.d.ts.map +0 -1
- package/dist/templates/agents/metadata.js +0 -101
- package/dist/templates/agents/metadata.js.map +0 -1
- package/dist/templates/commands/index.d.ts +0 -48
- package/dist/templates/commands/index.d.ts.map +0 -1
- package/dist/templates/commands/index.js +0 -167
- package/dist/templates/commands/index.js.map +0 -1
- package/dist/templates/commands/opencode/start.md.txt +0 -127
- package/dist/templates/hooks/index.d.ts +0 -33
- package/dist/templates/hooks/index.d.ts.map +0 -1
- package/dist/templates/hooks/index.js +0 -53
- package/dist/templates/hooks/index.js.map +0 -1
- package/dist/templates/scripts/index.d.ts +0 -36
- package/dist/templates/scripts/index.d.ts.map +0 -1
- package/dist/templates/scripts/index.js +0 -41
- package/dist/templates/scripts/index.js.map +0 -1
- /package/dist/{templates/commands/common/before-backend-dev.txt → .claude/commands/before-backend-dev.md} +0 -0
- /package/dist/{templates/commands/common/before-frontend-dev.txt → .claude/commands/before-frontend-dev.md} +0 -0
- /package/dist/{templates/commands/common/break-loop.txt → .claude/commands/break-loop.md} +0 -0
- /package/dist/{templates/commands/common/check-backend.txt → .claude/commands/check-backend.md} +0 -0
- /package/dist/{templates/commands/common/check-frontend.txt → .claude/commands/check-frontend.md} +0 -0
- /package/dist/{templates/commands/common/create-command.txt → .claude/commands/create-command.md} +0 -0
- /package/dist/{templates/commands/common/finish-work.txt → .claude/commands/finish-work.md} +0 -0
- /package/dist/{templates/commands/common/integrate-skill.txt → .claude/commands/integrate-skill.md} +0 -0
- /package/dist/{templates/commands/common/onboard-developer.txt → .claude/commands/onboard-developer.md} +0 -0
- /package/dist/{templates/commands/claude/parallel.md.txt → .claude/commands/parallel.md} +0 -0
- /package/dist/{templates/commands/common/record-agent-flow.txt → .claude/commands/record-agent-flow.md} +0 -0
- /package/dist/{templates/commands/claude/start.md.txt → .claude/commands/start.md} +0 -0
- /package/dist/{templates/hooks → .claude}/settings.json +0 -0
- /package/dist/{templates/commands/cursor/start.md.txt → .cursor/commands/start.md} +0 -0
- /package/dist/{templates/markdown/agent-traces-index.md.txt → .trellis/agent-traces/index.md} +0 -0
- /package/dist/{templates/scripts/add-session.sh.txt → .trellis/scripts/add-session.sh} +0 -0
- /package/dist/{templates/scripts/common/developer.sh.txt → .trellis/scripts/common/developer.sh} +0 -0
- /package/dist/{templates/scripts/common/git-context.sh.txt → .trellis/scripts/common/git-context.sh} +0 -0
- /package/dist/{templates/scripts/common/paths.sh.txt → .trellis/scripts/common/paths.sh} +0 -0
- /package/dist/{templates/scripts/common/worktree.sh.txt → .trellis/scripts/common/worktree.sh} +0 -0
- /package/dist/{templates/scripts/create-bootstrap.sh.txt → .trellis/scripts/create-bootstrap.sh} +0 -0
- /package/dist/{templates/scripts/get-context.sh.txt → .trellis/scripts/get-context.sh} +0 -0
- /package/dist/{templates/scripts/get-developer.sh.txt → .trellis/scripts/get-developer.sh} +0 -0
- /package/dist/{templates/scripts/init-developer.sh.txt → .trellis/scripts/init-developer.sh} +0 -0
- /package/dist/{templates/markdown/workflow.md.txt → .trellis/workflow.md} +0 -0
- /package/dist/templates/markdown/{agents.md.txt → agents.md} +0 -0
- /package/dist/templates/markdown/{init-agent.md.txt → init-agent.md} +0 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# Phase Management Utilities
|
|
4
|
+
# =============================================================================
|
|
5
|
+
# Centralized phase tracking for multi-agent pipeline
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
# source common/phase.sh
|
|
9
|
+
#
|
|
10
|
+
# get_current_phase "$feature_json" # Returns current phase number
|
|
11
|
+
# get_total_phases "$feature_json" # Returns total phase count
|
|
12
|
+
# get_phase_action "$feature_json" "$phase" # Returns action name for phase
|
|
13
|
+
# get_phase_info "$feature_json" # Returns "N/M (action)" format
|
|
14
|
+
# set_phase "$feature_json" "$phase" # Sets current_phase
|
|
15
|
+
# advance_phase "$feature_json" # Advances to next phase
|
|
16
|
+
# get_phase_for_action "$feature_json" "$action" # Returns phase number for action
|
|
17
|
+
# =============================================================================
|
|
18
|
+
|
|
19
|
+
# Get current phase number
|
|
20
|
+
get_current_phase() {
|
|
21
|
+
local feature_json="$1"
|
|
22
|
+
if [ ! -f "$feature_json" ]; then
|
|
23
|
+
echo "0"
|
|
24
|
+
return
|
|
25
|
+
fi
|
|
26
|
+
jq -r '.current_phase // 0' "$feature_json"
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
# Get total number of phases
|
|
30
|
+
get_total_phases() {
|
|
31
|
+
local feature_json="$1"
|
|
32
|
+
if [ ! -f "$feature_json" ]; then
|
|
33
|
+
echo "0"
|
|
34
|
+
return
|
|
35
|
+
fi
|
|
36
|
+
jq -r '.next_action | length // 0' "$feature_json"
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
# Get action name for a specific phase
|
|
40
|
+
get_phase_action() {
|
|
41
|
+
local feature_json="$1"
|
|
42
|
+
local phase="$2"
|
|
43
|
+
if [ ! -f "$feature_json" ]; then
|
|
44
|
+
echo "unknown"
|
|
45
|
+
return
|
|
46
|
+
fi
|
|
47
|
+
jq -r --argjson phase "$phase" '.next_action[] | select(.phase == $phase) | .action // "unknown"' "$feature_json"
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# Get formatted phase info: "N/M (action)"
|
|
51
|
+
get_phase_info() {
|
|
52
|
+
local feature_json="$1"
|
|
53
|
+
if [ ! -f "$feature_json" ]; then
|
|
54
|
+
echo "N/A"
|
|
55
|
+
return
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
local current_phase=$(get_current_phase "$feature_json")
|
|
59
|
+
local total_phases=$(get_total_phases "$feature_json")
|
|
60
|
+
local action_name=$(get_phase_action "$feature_json" "$current_phase")
|
|
61
|
+
|
|
62
|
+
if [ "$current_phase" = "0" ] || [ "$current_phase" = "null" ]; then
|
|
63
|
+
echo "0/${total_phases} (pending)"
|
|
64
|
+
else
|
|
65
|
+
echo "${current_phase}/${total_phases} (${action_name})"
|
|
66
|
+
fi
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
# Set current phase to a specific value
|
|
70
|
+
set_phase() {
|
|
71
|
+
local feature_json="$1"
|
|
72
|
+
local phase="$2"
|
|
73
|
+
|
|
74
|
+
if [ ! -f "$feature_json" ]; then
|
|
75
|
+
echo "Error: feature.json not found: $feature_json" >&2
|
|
76
|
+
return 1
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
jq --argjson phase "$phase" '.current_phase = $phase' "$feature_json" > "${feature_json}.tmp"
|
|
80
|
+
mv "${feature_json}.tmp" "$feature_json"
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
# Advance to next phase
|
|
84
|
+
advance_phase() {
|
|
85
|
+
local feature_json="$1"
|
|
86
|
+
|
|
87
|
+
if [ ! -f "$feature_json" ]; then
|
|
88
|
+
echo "Error: feature.json not found: $feature_json" >&2
|
|
89
|
+
return 1
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
local current=$(get_current_phase "$feature_json")
|
|
93
|
+
local total=$(get_total_phases "$feature_json")
|
|
94
|
+
local next=$((current + 1))
|
|
95
|
+
|
|
96
|
+
if [ "$next" -gt "$total" ]; then
|
|
97
|
+
echo "Warning: Already at final phase" >&2
|
|
98
|
+
return 0
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
set_phase "$feature_json" "$next"
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
# Get phase number for a specific action name
|
|
105
|
+
get_phase_for_action() {
|
|
106
|
+
local feature_json="$1"
|
|
107
|
+
local action="$2"
|
|
108
|
+
|
|
109
|
+
if [ ! -f "$feature_json" ]; then
|
|
110
|
+
echo "0"
|
|
111
|
+
return
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
jq -r --arg action "$action" '.next_action[] | select(.action == $action) | .phase // 0' "$feature_json"
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
# Map subagent type to action name
|
|
118
|
+
# Used by hooks to determine which action a subagent corresponds to
|
|
119
|
+
map_subagent_to_action() {
|
|
120
|
+
local subagent_type="$1"
|
|
121
|
+
|
|
122
|
+
case "$subagent_type" in
|
|
123
|
+
implement) echo "implement" ;;
|
|
124
|
+
check) echo "check" ;;
|
|
125
|
+
debug) echo "debug" ;;
|
|
126
|
+
research) echo "research" ;;
|
|
127
|
+
# finish uses check agent but is a different action
|
|
128
|
+
*) echo "$subagent_type" ;;
|
|
129
|
+
esac
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
# Check if a phase is completed (current_phase > phase)
|
|
133
|
+
is_phase_completed() {
|
|
134
|
+
local feature_json="$1"
|
|
135
|
+
local phase="$2"
|
|
136
|
+
|
|
137
|
+
local current=$(get_current_phase "$feature_json")
|
|
138
|
+
[ "$current" -gt "$phase" ]
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
# Check if we're at a specific action
|
|
142
|
+
is_current_action() {
|
|
143
|
+
local feature_json="$1"
|
|
144
|
+
local action="$2"
|
|
145
|
+
|
|
146
|
+
local current=$(get_current_phase "$feature_json")
|
|
147
|
+
local action_phase=$(get_phase_for_action "$feature_json" "$action")
|
|
148
|
+
|
|
149
|
+
[ "$current" = "$action_phase" ]
|
|
150
|
+
}
|
|
@@ -880,10 +880,14 @@ cmd_create_pr() {
|
|
|
880
880
|
echo -e "[DRY-RUN] Would update feature.json:"
|
|
881
881
|
echo -e " status: review"
|
|
882
882
|
echo -e " pr_url: ${pr_url}"
|
|
883
|
+
echo -e " current_phase: (set to create-pr phase)"
|
|
883
884
|
else
|
|
884
|
-
|
|
885
|
+
# Find the phase number for create-pr action
|
|
886
|
+
local create_pr_phase=$(jq -r '.next_action[] | select(.action == "create-pr") | .phase // 4' "$feature_json")
|
|
887
|
+
jq --arg url "$pr_url" --argjson phase "$create_pr_phase" \
|
|
888
|
+
'.status = "review" | .pr_url = $url | .current_phase = $phase' "$feature_json" > "${feature_json}.tmp"
|
|
885
889
|
mv "${feature_json}.tmp" "$feature_json"
|
|
886
|
-
echo -e "${GREEN}Feature status updated to 'review'${NC}"
|
|
890
|
+
echo -e "${GREEN}Feature status updated to 'review', phase ${create_pr_phase}${NC}"
|
|
887
891
|
fi
|
|
888
892
|
|
|
889
893
|
# In dry-run, reset the staging area
|
|
@@ -970,8 +974,9 @@ case "${1:-}" in
|
|
|
970
974
|
cmd_set_scope "$2" "$3"
|
|
971
975
|
;;
|
|
972
976
|
create-pr)
|
|
977
|
+
# Delegate to multi-agent/create-pr.sh
|
|
973
978
|
shift
|
|
974
|
-
|
|
979
|
+
"$SCRIPT_DIR/multi-agent/create-pr.sh" "$@"
|
|
975
980
|
;;
|
|
976
981
|
archive)
|
|
977
982
|
cmd_archive "$2"
|
|
@@ -155,6 +155,76 @@ remove_from_registry() {
|
|
|
155
155
|
log_info "Removed from registry"
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
+
# =============================================================================
|
|
159
|
+
# Cleanup from Registry Only (no worktree)
|
|
160
|
+
# =============================================================================
|
|
161
|
+
cleanup_registry_only() {
|
|
162
|
+
local search="$1"
|
|
163
|
+
|
|
164
|
+
AGENTS_DIR=$(get_agents_dir)
|
|
165
|
+
REGISTRY_FILE="${AGENTS_DIR}/registry.json"
|
|
166
|
+
|
|
167
|
+
if [ ! -f "$REGISTRY_FILE" ]; then
|
|
168
|
+
log_error "No registry found"
|
|
169
|
+
exit 1
|
|
170
|
+
fi
|
|
171
|
+
|
|
172
|
+
# Find agent by id or feature_dir containing search term
|
|
173
|
+
local agent_info=$(jq -r --arg search "$search" '.agents[] | select(.id == $search or (.feature_dir | contains($search)))' "$REGISTRY_FILE" 2>/dev/null | head -1)
|
|
174
|
+
|
|
175
|
+
if [ -z "$agent_info" ] || [ "$agent_info" = "null" ]; then
|
|
176
|
+
log_error "No agent found in registry matching: $search"
|
|
177
|
+
exit 1
|
|
178
|
+
fi
|
|
179
|
+
|
|
180
|
+
local agent_id=$(echo "$agent_info" | jq -r '.id')
|
|
181
|
+
local feature_dir=$(echo "$agent_info" | jq -r '.feature_dir')
|
|
182
|
+
local worktree_path=$(echo "$agent_info" | jq -r '.worktree_path')
|
|
183
|
+
|
|
184
|
+
echo ""
|
|
185
|
+
echo -e "${BLUE}=== Cleanup Agent (no worktree) ===${NC}"
|
|
186
|
+
echo " Agent ID: $agent_id"
|
|
187
|
+
echo " Feature Dir: $feature_dir"
|
|
188
|
+
echo ""
|
|
189
|
+
|
|
190
|
+
# Confirmation
|
|
191
|
+
if [ "$SKIP_CONFIRM" != "true" ]; then
|
|
192
|
+
if [ -t 0 ]; then
|
|
193
|
+
read -p "Archive feature and remove from registry? [y/N] " -n 1 -r
|
|
194
|
+
echo
|
|
195
|
+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
196
|
+
log_info "Aborted"
|
|
197
|
+
exit 0
|
|
198
|
+
fi
|
|
199
|
+
else
|
|
200
|
+
log_error "Non-interactive mode detected. Use -y to skip confirmation."
|
|
201
|
+
exit 1
|
|
202
|
+
fi
|
|
203
|
+
fi
|
|
204
|
+
|
|
205
|
+
# 1. Archive feature directory if exists
|
|
206
|
+
FEATURE_DIR_ABS="${PROJECT_ROOT}/${feature_dir}"
|
|
207
|
+
if [ -d "$FEATURE_DIR_ABS" ]; then
|
|
208
|
+
local features_dir=$(get_features_dir)
|
|
209
|
+
local archive_dir="$features_dir/archive"
|
|
210
|
+
local year_month=$(date +%Y-%m)
|
|
211
|
+
local month_dir="$archive_dir/$year_month"
|
|
212
|
+
|
|
213
|
+
mkdir -p "$month_dir"
|
|
214
|
+
|
|
215
|
+
local feature_name=$(basename "$feature_dir")
|
|
216
|
+
mv "$FEATURE_DIR_ABS" "$month_dir/"
|
|
217
|
+
log_success "Archived feature: $feature_name -> archive/$year_month/"
|
|
218
|
+
fi
|
|
219
|
+
|
|
220
|
+
# 2. Remove from registry
|
|
221
|
+
local updated=$(jq --arg id "$agent_id" '.agents = [.agents[] | select(.id != $id)]' "$REGISTRY_FILE")
|
|
222
|
+
echo "$updated" | jq '.' > "$REGISTRY_FILE"
|
|
223
|
+
log_success "Removed from registry: $agent_id"
|
|
224
|
+
|
|
225
|
+
log_success "Cleanup complete"
|
|
226
|
+
}
|
|
227
|
+
|
|
158
228
|
# =============================================================================
|
|
159
229
|
# Cleanup Single Worktree
|
|
160
230
|
# =============================================================================
|
|
@@ -169,8 +239,11 @@ cleanup_worktree() {
|
|
|
169
239
|
local worktree_path=$(echo "$worktree_info" | grep "^worktree " | cut -d' ' -f2-)
|
|
170
240
|
|
|
171
241
|
if [ -z "$worktree_path" ]; then
|
|
172
|
-
|
|
173
|
-
|
|
242
|
+
# No worktree found, try to cleanup from registry only
|
|
243
|
+
log_warn "No worktree found for: $branch"
|
|
244
|
+
log_info "Trying to cleanup from registry..."
|
|
245
|
+
cleanup_registry_only "$branch"
|
|
246
|
+
return
|
|
174
247
|
fi
|
|
175
248
|
|
|
176
249
|
echo ""
|
|
@@ -181,11 +254,17 @@ cleanup_worktree() {
|
|
|
181
254
|
|
|
182
255
|
# Confirmation
|
|
183
256
|
if [ "$SKIP_CONFIRM" != "true" ]; then
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
257
|
+
# Check if running interactively
|
|
258
|
+
if [ -t 0 ]; then
|
|
259
|
+
read -p "Remove this worktree? [y/N] " -n 1 -r
|
|
260
|
+
echo
|
|
261
|
+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
262
|
+
log_info "Aborted"
|
|
263
|
+
exit 0
|
|
264
|
+
fi
|
|
265
|
+
else
|
|
266
|
+
log_error "Non-interactive mode detected. Use -y to skip confirmation."
|
|
267
|
+
exit 1
|
|
189
268
|
fi
|
|
190
269
|
fi
|
|
191
270
|
|
|
@@ -243,11 +322,16 @@ cmd_merged() {
|
|
|
243
322
|
echo ""
|
|
244
323
|
|
|
245
324
|
if [ "$SKIP_CONFIRM" != "true" ]; then
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
325
|
+
if [ -t 0 ]; then
|
|
326
|
+
read -p "Remove these merged worktrees? [y/N] " -n 1 -r
|
|
327
|
+
echo
|
|
328
|
+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
329
|
+
log_info "Aborted"
|
|
330
|
+
exit 0
|
|
331
|
+
fi
|
|
332
|
+
else
|
|
333
|
+
log_error "Non-interactive mode detected. Use -y to skip confirmation."
|
|
334
|
+
exit 1
|
|
251
335
|
fi
|
|
252
336
|
fi
|
|
253
337
|
|
|
@@ -279,12 +363,17 @@ cmd_all() {
|
|
|
279
363
|
echo ""
|
|
280
364
|
|
|
281
365
|
if [ "$SKIP_CONFIRM" != "true" ]; then
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
366
|
+
if [ -t 0 ]; then
|
|
367
|
+
echo -e "${RED}WARNING: This will remove ALL worktrees!${NC}"
|
|
368
|
+
read -p "Are you sure? [y/N] " -n 1 -r
|
|
369
|
+
echo
|
|
370
|
+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
371
|
+
log_info "Aborted"
|
|
372
|
+
exit 0
|
|
373
|
+
fi
|
|
374
|
+
else
|
|
375
|
+
log_error "Non-interactive mode detected. Use -y to skip confirmation."
|
|
376
|
+
exit 1
|
|
288
377
|
fi
|
|
289
378
|
fi
|
|
290
379
|
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# Multi-Agent Pipeline: Create PR
|
|
4
|
+
# =============================================================================
|
|
5
|
+
# Usage:
|
|
6
|
+
# ./create-pr.sh [feature-dir] [--dry-run]
|
|
7
|
+
#
|
|
8
|
+
# This script:
|
|
9
|
+
# 1. Stages and commits all changes (excluding agent-traces)
|
|
10
|
+
# 2. Pushes to origin
|
|
11
|
+
# 3. Creates a Draft PR using `gh pr create`
|
|
12
|
+
# 4. Updates feature.json with status="review", pr_url, and current_phase
|
|
13
|
+
#
|
|
14
|
+
# Note: This is the only action that performs git commit, as it's the final
|
|
15
|
+
# step after all implementation and checks are complete.
|
|
16
|
+
# =============================================================================
|
|
17
|
+
|
|
18
|
+
set -e
|
|
19
|
+
|
|
20
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
21
|
+
source "$SCRIPT_DIR/../common/paths.sh"
|
|
22
|
+
source "$SCRIPT_DIR/../common/phase.sh"
|
|
23
|
+
|
|
24
|
+
# Colors
|
|
25
|
+
RED='\033[0;31m'
|
|
26
|
+
GREEN='\033[0;32m'
|
|
27
|
+
YELLOW='\033[1;33m'
|
|
28
|
+
BLUE='\033[0;34m'
|
|
29
|
+
NC='\033[0m'
|
|
30
|
+
|
|
31
|
+
REPO_ROOT=$(get_repo_root)
|
|
32
|
+
|
|
33
|
+
# =============================================================================
|
|
34
|
+
# Parse Arguments
|
|
35
|
+
# =============================================================================
|
|
36
|
+
TARGET_DIR=""
|
|
37
|
+
DRY_RUN=false
|
|
38
|
+
|
|
39
|
+
while [[ $# -gt 0 ]]; do
|
|
40
|
+
case "$1" in
|
|
41
|
+
--dry-run)
|
|
42
|
+
DRY_RUN=true
|
|
43
|
+
shift
|
|
44
|
+
;;
|
|
45
|
+
-h|--help)
|
|
46
|
+
echo "Usage: $0 [feature-dir] [--dry-run]"
|
|
47
|
+
echo ""
|
|
48
|
+
echo "Options:"
|
|
49
|
+
echo " --dry-run Show what would be done without making changes"
|
|
50
|
+
echo " -h, --help Show this help message"
|
|
51
|
+
exit 0
|
|
52
|
+
;;
|
|
53
|
+
*)
|
|
54
|
+
if [[ -z "$TARGET_DIR" ]]; then
|
|
55
|
+
TARGET_DIR="$1"
|
|
56
|
+
fi
|
|
57
|
+
shift
|
|
58
|
+
;;
|
|
59
|
+
esac
|
|
60
|
+
done
|
|
61
|
+
|
|
62
|
+
# =============================================================================
|
|
63
|
+
# Get Feature Directory
|
|
64
|
+
# =============================================================================
|
|
65
|
+
if [[ -z "$TARGET_DIR" ]]; then
|
|
66
|
+
# Try to get from .current-feature
|
|
67
|
+
CURRENT_FEATURE_FILE="$REPO_ROOT/.trellis/.current-feature"
|
|
68
|
+
if [[ -f "$CURRENT_FEATURE_FILE" ]]; then
|
|
69
|
+
TARGET_DIR=$(cat "$CURRENT_FEATURE_FILE")
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
if [[ -z "$TARGET_DIR" ]]; then
|
|
73
|
+
echo -e "${RED}Error: No feature directory specified and no current feature set${NC}"
|
|
74
|
+
echo "Usage: $0 [feature-dir] [--dry-run]"
|
|
75
|
+
exit 1
|
|
76
|
+
fi
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
# Support relative paths
|
|
80
|
+
if [[ ! "$TARGET_DIR" = /* ]]; then
|
|
81
|
+
TARGET_DIR="$REPO_ROOT/$TARGET_DIR"
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
FEATURE_JSON="$TARGET_DIR/feature.json"
|
|
85
|
+
if [[ ! -f "$FEATURE_JSON" ]]; then
|
|
86
|
+
echo -e "${RED}Error: feature.json not found at $TARGET_DIR${NC}"
|
|
87
|
+
exit 1
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
# =============================================================================
|
|
91
|
+
# Main
|
|
92
|
+
# =============================================================================
|
|
93
|
+
echo -e "${BLUE}=== Create PR ===${NC}"
|
|
94
|
+
if [[ "$DRY_RUN" == "true" ]]; then
|
|
95
|
+
echo -e "${YELLOW}[DRY-RUN MODE] No actual changes will be made${NC}"
|
|
96
|
+
fi
|
|
97
|
+
echo ""
|
|
98
|
+
|
|
99
|
+
# Read feature config
|
|
100
|
+
FEATURE_NAME=$(jq -r '.name' "$FEATURE_JSON")
|
|
101
|
+
BASE_BRANCH=$(jq -r '.base_branch // "main"' "$FEATURE_JSON")
|
|
102
|
+
SCOPE=$(jq -r '.scope // "core"' "$FEATURE_JSON")
|
|
103
|
+
DEV_TYPE=$(jq -r '.dev_type // "feature"' "$FEATURE_JSON")
|
|
104
|
+
|
|
105
|
+
# Map dev_type to commit prefix
|
|
106
|
+
case "$DEV_TYPE" in
|
|
107
|
+
feature|frontend|backend|fullstack) COMMIT_PREFIX="feat" ;;
|
|
108
|
+
bugfix|fix) COMMIT_PREFIX="fix" ;;
|
|
109
|
+
refactor) COMMIT_PREFIX="refactor" ;;
|
|
110
|
+
docs) COMMIT_PREFIX="docs" ;;
|
|
111
|
+
test) COMMIT_PREFIX="test" ;;
|
|
112
|
+
*) COMMIT_PREFIX="feat" ;;
|
|
113
|
+
esac
|
|
114
|
+
|
|
115
|
+
echo -e "Feature: ${FEATURE_NAME}"
|
|
116
|
+
echo -e "Base branch: ${BASE_BRANCH}"
|
|
117
|
+
echo -e "Scope: ${SCOPE}"
|
|
118
|
+
echo -e "Commit prefix: ${COMMIT_PREFIX}"
|
|
119
|
+
echo ""
|
|
120
|
+
|
|
121
|
+
# Get current branch
|
|
122
|
+
CURRENT_BRANCH=$(git branch --show-current)
|
|
123
|
+
echo -e "Current branch: ${CURRENT_BRANCH}"
|
|
124
|
+
|
|
125
|
+
# Check for changes
|
|
126
|
+
echo -e "${YELLOW}Checking for changes...${NC}"
|
|
127
|
+
|
|
128
|
+
# Stage changes (even in dry-run to detect what would be committed)
|
|
129
|
+
git add -A
|
|
130
|
+
|
|
131
|
+
# Exclude agent traces and temp files
|
|
132
|
+
git reset ".trellis/agent-traces/" 2>/dev/null || true
|
|
133
|
+
git reset .agent-log .agent-prompt .agent-runner.sh 2>/dev/null || true
|
|
134
|
+
|
|
135
|
+
# Check if there are staged changes
|
|
136
|
+
if git diff --cached --quiet 2>/dev/null; then
|
|
137
|
+
echo -e "${YELLOW}No staged changes to commit${NC}"
|
|
138
|
+
|
|
139
|
+
# Check for unpushed commits
|
|
140
|
+
UNPUSHED=$(git log "origin/${CURRENT_BRANCH}..HEAD" --oneline 2>/dev/null | wc -l | tr -d ' ' || echo "0")
|
|
141
|
+
if [[ "$UNPUSHED" -eq 0 ]] 2>/dev/null; then
|
|
142
|
+
# In dry-run, also reset the staging
|
|
143
|
+
if [[ "$DRY_RUN" == "true" ]]; then
|
|
144
|
+
git reset HEAD >/dev/null 2>&1 || true
|
|
145
|
+
fi
|
|
146
|
+
echo -e "${RED}No changes to create PR${NC}"
|
|
147
|
+
exit 1
|
|
148
|
+
fi
|
|
149
|
+
echo -e "Found ${UNPUSHED} unpushed commit(s)"
|
|
150
|
+
else
|
|
151
|
+
# Commit changes
|
|
152
|
+
echo -e "${YELLOW}Committing changes...${NC}"
|
|
153
|
+
COMMIT_MSG="${COMMIT_PREFIX}(${SCOPE}): ${FEATURE_NAME}"
|
|
154
|
+
|
|
155
|
+
if [[ "$DRY_RUN" == "true" ]]; then
|
|
156
|
+
echo -e "[DRY-RUN] Would commit with message: ${COMMIT_MSG}"
|
|
157
|
+
echo -e "[DRY-RUN] Staged files:"
|
|
158
|
+
git diff --cached --name-only | sed 's/^/ - /'
|
|
159
|
+
else
|
|
160
|
+
git commit -m "$COMMIT_MSG"
|
|
161
|
+
echo -e "${GREEN}Committed: ${COMMIT_MSG}${NC}"
|
|
162
|
+
fi
|
|
163
|
+
fi
|
|
164
|
+
|
|
165
|
+
# Push to remote
|
|
166
|
+
echo -e "${YELLOW}Pushing to remote...${NC}"
|
|
167
|
+
if [[ "$DRY_RUN" == "true" ]]; then
|
|
168
|
+
echo -e "[DRY-RUN] Would push to: origin/${CURRENT_BRANCH}"
|
|
169
|
+
else
|
|
170
|
+
git push -u origin "$CURRENT_BRANCH"
|
|
171
|
+
echo -e "${GREEN}Pushed to origin/${CURRENT_BRANCH}${NC}"
|
|
172
|
+
fi
|
|
173
|
+
|
|
174
|
+
# Create PR
|
|
175
|
+
echo -e "${YELLOW}Creating PR...${NC}"
|
|
176
|
+
PR_TITLE="${COMMIT_PREFIX}(${SCOPE}): ${FEATURE_NAME}"
|
|
177
|
+
PR_URL=""
|
|
178
|
+
|
|
179
|
+
if [[ "$DRY_RUN" == "true" ]]; then
|
|
180
|
+
echo -e "[DRY-RUN] Would create PR:"
|
|
181
|
+
echo -e " Title: ${PR_TITLE}"
|
|
182
|
+
echo -e " Base: ${BASE_BRANCH}"
|
|
183
|
+
echo -e " Head: ${CURRENT_BRANCH}"
|
|
184
|
+
if [[ -f "$TARGET_DIR/prd.md" ]]; then
|
|
185
|
+
echo -e " Body: (from prd.md)"
|
|
186
|
+
fi
|
|
187
|
+
PR_URL="https://github.com/example/repo/pull/DRY-RUN"
|
|
188
|
+
else
|
|
189
|
+
# Check if PR already exists
|
|
190
|
+
EXISTING_PR=$(gh pr list --head "$CURRENT_BRANCH" --base "$BASE_BRANCH" --json url --jq '.[0].url' 2>/dev/null || echo "")
|
|
191
|
+
|
|
192
|
+
if [[ -n "$EXISTING_PR" ]]; then
|
|
193
|
+
echo -e "${YELLOW}PR already exists: ${EXISTING_PR}${NC}"
|
|
194
|
+
PR_URL="$EXISTING_PR"
|
|
195
|
+
else
|
|
196
|
+
# Read PRD as PR body
|
|
197
|
+
PR_BODY=""
|
|
198
|
+
if [[ -f "$TARGET_DIR/prd.md" ]]; then
|
|
199
|
+
PR_BODY=$(cat "$TARGET_DIR/prd.md")
|
|
200
|
+
fi
|
|
201
|
+
|
|
202
|
+
# Create PR
|
|
203
|
+
PR_URL=$(gh pr create \
|
|
204
|
+
--draft \
|
|
205
|
+
--base "$BASE_BRANCH" \
|
|
206
|
+
--title "$PR_TITLE" \
|
|
207
|
+
--body "$PR_BODY" \
|
|
208
|
+
2>&1)
|
|
209
|
+
|
|
210
|
+
echo -e "${GREEN}PR created: ${PR_URL}${NC}"
|
|
211
|
+
fi
|
|
212
|
+
fi
|
|
213
|
+
|
|
214
|
+
# Update feature.json
|
|
215
|
+
echo -e "${YELLOW}Updating feature status...${NC}"
|
|
216
|
+
if [[ "$DRY_RUN" == "true" ]]; then
|
|
217
|
+
echo -e "[DRY-RUN] Would update feature.json:"
|
|
218
|
+
echo -e " status: review"
|
|
219
|
+
echo -e " pr_url: ${PR_URL}"
|
|
220
|
+
echo -e " current_phase: (set to create-pr phase)"
|
|
221
|
+
else
|
|
222
|
+
# Get the phase number for create-pr action using common/phase.sh
|
|
223
|
+
CREATE_PR_PHASE=$(get_phase_for_action "$FEATURE_JSON" "create-pr")
|
|
224
|
+
if [[ -z "$CREATE_PR_PHASE" ]] || [[ "$CREATE_PR_PHASE" == "0" ]]; then
|
|
225
|
+
CREATE_PR_PHASE=4 # Default fallback
|
|
226
|
+
fi
|
|
227
|
+
|
|
228
|
+
jq --arg url "$PR_URL" --argjson phase "$CREATE_PR_PHASE" \
|
|
229
|
+
'.status = "review" | .pr_url = $url | .current_phase = $phase' "$FEATURE_JSON" > "${FEATURE_JSON}.tmp"
|
|
230
|
+
mv "${FEATURE_JSON}.tmp" "$FEATURE_JSON"
|
|
231
|
+
echo -e "${GREEN}Feature status updated to 'review', phase ${CREATE_PR_PHASE}${NC}"
|
|
232
|
+
fi
|
|
233
|
+
|
|
234
|
+
# In dry-run, reset the staging area
|
|
235
|
+
if [[ "$DRY_RUN" == "true" ]]; then
|
|
236
|
+
git reset HEAD >/dev/null 2>&1 || true
|
|
237
|
+
fi
|
|
238
|
+
|
|
239
|
+
echo ""
|
|
240
|
+
echo -e "${GREEN}=== PR Created Successfully ===${NC}"
|
|
241
|
+
echo -e "PR URL: ${PR_URL}"
|