create-merlin-brain 2.3.3 → 2.5.0
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/files/CLAUDE.md +38 -0
- package/files/agents/merlin.md +49 -53
- package/files/commands/merlin/route.md +189 -0
- package/files/loop/lib/agents.sh +603 -0
- package/files/loop/lib/boot.sh +453 -0
- package/files/loop/lib/discuss.sh +224 -0
- package/files/loop/lib/modes.sh +294 -0
- package/files/loop/lib/session-end.sh +248 -0
- package/files/loop/lib/sights.sh +725 -0
- package/files/loop/lib/timeout.sh +207 -0
- package/files/loop/lib/tui.sh +388 -0
- package/files/loop/merlin-loop.sh +311 -16
- package/files/loop/prompts/PROMPT_DISCUSS.md +102 -0
- package/files/loop/prompts/PROMPT_build.md +152 -2
- package/package.json +1 -1
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Merlin Loop - Timeout Handling
|
|
4
|
+
# Provides timeout with countdown for pause points, enabling AFK operation
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
8
|
+
# Timeout Configuration
|
|
9
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
10
|
+
|
|
11
|
+
# Default timeout: 5 minutes (300 seconds)
|
|
12
|
+
PAUSE_TIMEOUT="${MERLIN_PAUSE_TIMEOUT:-300}"
|
|
13
|
+
|
|
14
|
+
# Default action when timeout expires
|
|
15
|
+
DEFAULT_TIMEOUT_ACTION="${MERLIN_DEFAULT_ACTION:-continue}"
|
|
16
|
+
|
|
17
|
+
# Whether to show countdown (can be disabled for scripted use)
|
|
18
|
+
SHOW_COUNTDOWN="${MERLIN_SHOW_COUNTDOWN:-true}"
|
|
19
|
+
|
|
20
|
+
# Whether timeout is enabled
|
|
21
|
+
TIMEOUT_ENABLED="${MERLIN_TIMEOUT_ENABLED:-true}"
|
|
22
|
+
|
|
23
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
24
|
+
# Timeout Control
|
|
25
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
26
|
+
|
|
27
|
+
# Disable timeout (for interactive mode)
|
|
28
|
+
disable_timeout() {
|
|
29
|
+
TIMEOUT_ENABLED="false"
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# Enable timeout
|
|
33
|
+
enable_timeout() {
|
|
34
|
+
TIMEOUT_ENABLED="true"
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
# Set timeout value
|
|
38
|
+
set_timeout() {
|
|
39
|
+
local seconds="$1"
|
|
40
|
+
if [[ "$seconds" =~ ^[0-9]+$ ]]; then
|
|
41
|
+
PAUSE_TIMEOUT="$seconds"
|
|
42
|
+
else
|
|
43
|
+
echo -e "${RED}Invalid timeout value: $seconds${RESET}" >&2
|
|
44
|
+
return 1
|
|
45
|
+
fi
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
49
|
+
# Countdown Display
|
|
50
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
51
|
+
|
|
52
|
+
# Format seconds as M:SS
|
|
53
|
+
format_time() {
|
|
54
|
+
local seconds="$1"
|
|
55
|
+
local minutes=$((seconds / 60))
|
|
56
|
+
local secs=$((seconds % 60))
|
|
57
|
+
printf "%d:%02d" "$minutes" "$secs"
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# Display countdown timer (updates in place)
|
|
61
|
+
countdown_display() {
|
|
62
|
+
local remaining="$1"
|
|
63
|
+
local default_action="$2"
|
|
64
|
+
local formatted
|
|
65
|
+
formatted=$(format_time "$remaining")
|
|
66
|
+
|
|
67
|
+
# Use carriage return to update in place
|
|
68
|
+
printf "\r⏱️ Auto-%s in %s... Press any key to respond " "$default_action" "$formatted"
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
# Clear the countdown line
|
|
72
|
+
clear_countdown() {
|
|
73
|
+
# Clear to end of line
|
|
74
|
+
printf "\r\033[K"
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
78
|
+
# Timeout Read
|
|
79
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
80
|
+
|
|
81
|
+
# Read with timeout and countdown
|
|
82
|
+
# Args: $1 = prompt, $2 = default value, $3 = custom timeout (optional)
|
|
83
|
+
# Returns: User input or default value via stdout
|
|
84
|
+
# Exit code: 0 = user input, 1 = timeout (default used)
|
|
85
|
+
read_with_timeout() {
|
|
86
|
+
local prompt="$1"
|
|
87
|
+
local default="$2"
|
|
88
|
+
local timeout="${3:-$PAUSE_TIMEOUT}"
|
|
89
|
+
|
|
90
|
+
# If timeout disabled, use regular read
|
|
91
|
+
if [ "$TIMEOUT_ENABLED" != "true" ] || [ "$timeout" -eq 0 ]; then
|
|
92
|
+
echo -n "$prompt"
|
|
93
|
+
read -r response
|
|
94
|
+
echo "${response:-$default}"
|
|
95
|
+
return 0
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
# Show what will happen on timeout
|
|
99
|
+
echo ""
|
|
100
|
+
echo -e "${YELLOW}⏱️ Will auto-${default} in $(format_time "$timeout") if no response${RESET}"
|
|
101
|
+
echo ""
|
|
102
|
+
echo -n "$prompt"
|
|
103
|
+
|
|
104
|
+
local remaining="$timeout"
|
|
105
|
+
local response=""
|
|
106
|
+
local timed_out=false
|
|
107
|
+
|
|
108
|
+
# Save cursor position
|
|
109
|
+
tput sc 2>/dev/null || true
|
|
110
|
+
|
|
111
|
+
while [ "$remaining" -gt 0 ]; do
|
|
112
|
+
# Try to read with 1-second timeout
|
|
113
|
+
if read -t 1 -r response 2>/dev/null; then
|
|
114
|
+
clear_countdown
|
|
115
|
+
# User provided input
|
|
116
|
+
echo "${response:-$default}"
|
|
117
|
+
return 0
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
# Decrement and show countdown
|
|
121
|
+
((remaining--))
|
|
122
|
+
|
|
123
|
+
if [ "$SHOW_COUNTDOWN" = "true" ] && [ "$remaining" -gt 0 ]; then
|
|
124
|
+
# Restore cursor and show countdown
|
|
125
|
+
tput rc 2>/dev/null || printf "\r"
|
|
126
|
+
countdown_display "$remaining" "$default"
|
|
127
|
+
fi
|
|
128
|
+
done
|
|
129
|
+
|
|
130
|
+
# Timeout reached
|
|
131
|
+
timed_out=true
|
|
132
|
+
clear_countdown
|
|
133
|
+
echo ""
|
|
134
|
+
echo -e "${YELLOW}⏱️ Timeout reached. Auto-${default}.${RESET}"
|
|
135
|
+
|
|
136
|
+
# Log timeout event
|
|
137
|
+
log_timeout_event "$default"
|
|
138
|
+
|
|
139
|
+
echo "$default"
|
|
140
|
+
return 1
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
144
|
+
# Timeout Logging
|
|
145
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
146
|
+
|
|
147
|
+
# Log a timeout event to history
|
|
148
|
+
log_timeout_event() {
|
|
149
|
+
local action="$1"
|
|
150
|
+
local now
|
|
151
|
+
now=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
152
|
+
|
|
153
|
+
if [ -n "$HISTORY_FILE" ] && [ -f "$HISTORY_FILE" ]; then
|
|
154
|
+
echo "{\"type\":\"timeout\",\"action\":\"$action\",\"timeout\":$PAUSE_TIMEOUT,\"timestamp\":\"$now\"}" >> "$HISTORY_FILE"
|
|
155
|
+
fi
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
159
|
+
# Get Default Action for Pause Reason
|
|
160
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
161
|
+
|
|
162
|
+
# Determine the appropriate default action based on pause reason
|
|
163
|
+
# Args: $1 = pause reason (from LAST_PAUSE_REASON)
|
|
164
|
+
# Returns: default action string
|
|
165
|
+
get_default_for_pause() {
|
|
166
|
+
local reason="${1:-unknown}"
|
|
167
|
+
|
|
168
|
+
case "$reason" in
|
|
169
|
+
"checkpoint")
|
|
170
|
+
# Explicit checkpoints should NOT auto-continue - they're deliberate
|
|
171
|
+
echo "quit"
|
|
172
|
+
;;
|
|
173
|
+
"decision")
|
|
174
|
+
# Decisions: proceed with Claude's recommendation
|
|
175
|
+
echo "continue"
|
|
176
|
+
;;
|
|
177
|
+
"low_confidence")
|
|
178
|
+
# Low confidence: trust Claude's judgment, continue
|
|
179
|
+
echo "continue"
|
|
180
|
+
;;
|
|
181
|
+
"periodic")
|
|
182
|
+
# Periodic checkpoints: continue is fine
|
|
183
|
+
echo "continue"
|
|
184
|
+
;;
|
|
185
|
+
"interactive")
|
|
186
|
+
# Interactive mode: continue (though usually timeout is disabled)
|
|
187
|
+
echo "continue"
|
|
188
|
+
;;
|
|
189
|
+
*)
|
|
190
|
+
# Unknown: default to continue
|
|
191
|
+
echo "continue"
|
|
192
|
+
;;
|
|
193
|
+
esac
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
197
|
+
# Status Display
|
|
198
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
199
|
+
|
|
200
|
+
# Get timeout status for display
|
|
201
|
+
get_timeout_status() {
|
|
202
|
+
if [ "$TIMEOUT_ENABLED" = "true" ]; then
|
|
203
|
+
echo "$(format_time "$PAUSE_TIMEOUT") (enabled)"
|
|
204
|
+
else
|
|
205
|
+
echo "Disabled (waits indefinitely)"
|
|
206
|
+
fi
|
|
207
|
+
}
|
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Merlin Loop - Interactive TUI
|
|
4
|
+
# Beautiful terminal interface for pause/continue control
|
|
5
|
+
# Part of Merlin Pro v1.0
|
|
6
|
+
#
|
|
7
|
+
|
|
8
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
9
|
+
# ANSI Colors and Styles
|
|
10
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
11
|
+
|
|
12
|
+
# Colors (if not already defined)
|
|
13
|
+
: "${RESET:=\033[0m}"
|
|
14
|
+
: "${BOLD:=\033[1m}"
|
|
15
|
+
: "${DIM:=\033[2m}"
|
|
16
|
+
: "${ITALIC:=\033[3m}"
|
|
17
|
+
: "${RED:=\033[31m}"
|
|
18
|
+
: "${GREEN:=\033[32m}"
|
|
19
|
+
: "${YELLOW:=\033[33m}"
|
|
20
|
+
: "${BLUE:=\033[34m}"
|
|
21
|
+
: "${MAGENTA:=\033[35m}"
|
|
22
|
+
: "${CYAN:=\033[36m}"
|
|
23
|
+
: "${WHITE:=\033[37m}"
|
|
24
|
+
: "${BG_BLACK:=\033[40m}"
|
|
25
|
+
: "${BG_BLUE:=\033[44m}"
|
|
26
|
+
: "${BG_GREEN:=\033[42m}"
|
|
27
|
+
|
|
28
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
29
|
+
# Progress Bar
|
|
30
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
31
|
+
|
|
32
|
+
# Draw a progress bar
|
|
33
|
+
# Usage: draw_progress_bar 5 10 40 (5 of 10, width 40)
|
|
34
|
+
draw_progress_bar() {
|
|
35
|
+
local current=${1:-0}
|
|
36
|
+
local total=${2:-100}
|
|
37
|
+
local width=${3:-40}
|
|
38
|
+
local label=${4:-"Progress"}
|
|
39
|
+
|
|
40
|
+
# Calculate percentage
|
|
41
|
+
local percent=0
|
|
42
|
+
if [ "$total" -gt 0 ]; then
|
|
43
|
+
percent=$((current * 100 / total))
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
# Calculate filled width
|
|
47
|
+
local filled=$((current * width / total))
|
|
48
|
+
if [ "$filled" -gt "$width" ]; then
|
|
49
|
+
filled=$width
|
|
50
|
+
fi
|
|
51
|
+
local empty=$((width - filled))
|
|
52
|
+
|
|
53
|
+
# Build the bar
|
|
54
|
+
local bar=""
|
|
55
|
+
for ((i=0; i<filled; i++)); do
|
|
56
|
+
bar="${bar}█"
|
|
57
|
+
done
|
|
58
|
+
for ((i=0; i<empty; i++)); do
|
|
59
|
+
bar="${bar}░"
|
|
60
|
+
done
|
|
61
|
+
|
|
62
|
+
# Color based on progress
|
|
63
|
+
local color="${CYAN}"
|
|
64
|
+
if [ "$percent" -ge 80 ]; then
|
|
65
|
+
color="${GREEN}"
|
|
66
|
+
elif [ "$percent" -ge 50 ]; then
|
|
67
|
+
color="${YELLOW}"
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
echo -e "${label}: ${color}${bar}${RESET} ${percent}% (${current}/${total})"
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
74
|
+
# Spinner Animation
|
|
75
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
76
|
+
|
|
77
|
+
SPINNER_PID=""
|
|
78
|
+
SPINNER_FRAMES=("⠋" "⠙" "⠹" "⠸" "⠼" "⠴" "⠦" "⠧" "⠇" "⠏")
|
|
79
|
+
|
|
80
|
+
# Start spinner with message
|
|
81
|
+
# Usage: start_spinner "Loading..."
|
|
82
|
+
start_spinner() {
|
|
83
|
+
local message="${1:-Working...}"
|
|
84
|
+
|
|
85
|
+
(
|
|
86
|
+
local i=0
|
|
87
|
+
while true; do
|
|
88
|
+
printf "\r${CYAN}${SPINNER_FRAMES[$i]}${RESET} %s" "$message"
|
|
89
|
+
i=$(( (i+1) % ${#SPINNER_FRAMES[@]} ))
|
|
90
|
+
sleep 0.1
|
|
91
|
+
done
|
|
92
|
+
) &
|
|
93
|
+
|
|
94
|
+
SPINNER_PID=$!
|
|
95
|
+
disown $SPINNER_PID 2>/dev/null || true
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
# Stop spinner with result
|
|
99
|
+
# Usage: stop_spinner "Done!" or stop_spinner "Failed!" "error"
|
|
100
|
+
stop_spinner() {
|
|
101
|
+
local message="${1:-Done}"
|
|
102
|
+
local status="${2:-success}"
|
|
103
|
+
|
|
104
|
+
if [ -n "$SPINNER_PID" ] && kill -0 "$SPINNER_PID" 2>/dev/null; then
|
|
105
|
+
kill "$SPINNER_PID" 2>/dev/null
|
|
106
|
+
wait "$SPINNER_PID" 2>/dev/null || true
|
|
107
|
+
SPINNER_PID=""
|
|
108
|
+
fi
|
|
109
|
+
|
|
110
|
+
# Clear the line and print result
|
|
111
|
+
printf "\r%-60s\r" " "
|
|
112
|
+
|
|
113
|
+
if [ "$status" == "success" ]; then
|
|
114
|
+
echo -e "${GREEN}✓${RESET} $message"
|
|
115
|
+
elif [ "$status" == "error" ]; then
|
|
116
|
+
echo -e "${RED}✗${RESET} $message"
|
|
117
|
+
elif [ "$status" == "warning" ]; then
|
|
118
|
+
echo -e "${YELLOW}⚠${RESET} $message"
|
|
119
|
+
else
|
|
120
|
+
echo -e "$message"
|
|
121
|
+
fi
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
125
|
+
# Interactive Pause Menu
|
|
126
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
127
|
+
|
|
128
|
+
TUI_LAST_ACTION=""
|
|
129
|
+
|
|
130
|
+
# Show the pause menu and wait for input
|
|
131
|
+
# Returns: 0 for continue, 1 for quit, 2 for pause
|
|
132
|
+
show_pause_menu() {
|
|
133
|
+
local task_completed=${1:-0}
|
|
134
|
+
local task_total=${2:-0}
|
|
135
|
+
local current_task_name="${3:-Next task}"
|
|
136
|
+
local timeout=${4:-$PAUSE_TIMEOUT}
|
|
137
|
+
|
|
138
|
+
echo ""
|
|
139
|
+
echo -e "${MAGENTA}╔═══════════════════════════════════════════════════════════════════╗${RESET}"
|
|
140
|
+
echo -e "${MAGENTA}║${RESET} ${BOLD}🔮 MERLIN LOOP${RESET} ${MAGENTA}║${RESET}"
|
|
141
|
+
echo -e "${MAGENTA}╠═══════════════════════════════════════════════════════════════════╣${RESET}"
|
|
142
|
+
echo -e "${MAGENTA}║${RESET} ${MAGENTA}║${RESET}"
|
|
143
|
+
|
|
144
|
+
# Progress
|
|
145
|
+
if [ "$task_total" -gt 0 ]; then
|
|
146
|
+
local progress_bar
|
|
147
|
+
progress_bar=$(draw_progress_bar "$task_completed" "$task_total" 30 "Tasks")
|
|
148
|
+
printf "${MAGENTA}║${RESET} %-65s ${MAGENTA}║${RESET}\n" "$progress_bar"
|
|
149
|
+
fi
|
|
150
|
+
|
|
151
|
+
# Next task
|
|
152
|
+
printf "${MAGENTA}║${RESET} ${CYAN}Next:${RESET} %-58s ${MAGENTA}║${RESET}\n" "${current_task_name:0:58}"
|
|
153
|
+
|
|
154
|
+
echo -e "${MAGENTA}║${RESET} ${MAGENTA}║${RESET}"
|
|
155
|
+
echo -e "${MAGENTA}╠═══════════════════════════════════════════════════════════════════╣${RESET}"
|
|
156
|
+
echo -e "${MAGENTA}║${RESET} ${MAGENTA}║${RESET}"
|
|
157
|
+
echo -e "${MAGENTA}║${RESET} ${GREEN}[Enter]${RESET} Continue ${YELLOW}[p]${RESET} Pause ${RED}[q]${RESET} Quit ${CYAN}[s]${RESET} Status ${MAGENTA}║${RESET}"
|
|
158
|
+
echo -e "${MAGENTA}║${RESET} ${MAGENTA}║${RESET}"
|
|
159
|
+
echo -e "${MAGENTA}╚═══════════════════════════════════════════════════════════════════╝${RESET}"
|
|
160
|
+
echo ""
|
|
161
|
+
|
|
162
|
+
# Show timeout if enabled
|
|
163
|
+
if [ "$TIMEOUT_ENABLED" == "true" ] && [ "$timeout" -gt 0 ]; then
|
|
164
|
+
echo -e "${DIM}Auto-continue in ${timeout}s (press any key to interact)${RESET}"
|
|
165
|
+
fi
|
|
166
|
+
|
|
167
|
+
# Read input with timeout
|
|
168
|
+
local input=""
|
|
169
|
+
if [ "$TIMEOUT_ENABLED" == "true" ] && [ "$timeout" -gt 0 ]; then
|
|
170
|
+
read -t "$timeout" -n 1 input 2>/dev/null || true
|
|
171
|
+
else
|
|
172
|
+
read -n 1 input 2>/dev/null || true
|
|
173
|
+
fi
|
|
174
|
+
|
|
175
|
+
# Handle input
|
|
176
|
+
case "$input" in
|
|
177
|
+
""|$'\n')
|
|
178
|
+
# Enter or timeout - continue
|
|
179
|
+
TUI_LAST_ACTION="continue"
|
|
180
|
+
return 0
|
|
181
|
+
;;
|
|
182
|
+
"q"|"Q")
|
|
183
|
+
# Quit
|
|
184
|
+
TUI_LAST_ACTION="quit"
|
|
185
|
+
echo ""
|
|
186
|
+
echo -e "${YELLOW}Quitting gracefully...${RESET}"
|
|
187
|
+
return 1
|
|
188
|
+
;;
|
|
189
|
+
"p"|"P")
|
|
190
|
+
# Pause
|
|
191
|
+
TUI_LAST_ACTION="pause"
|
|
192
|
+
show_pause_screen
|
|
193
|
+
return 2
|
|
194
|
+
;;
|
|
195
|
+
"s"|"S")
|
|
196
|
+
# Status
|
|
197
|
+
TUI_LAST_ACTION="status"
|
|
198
|
+
show_status_screen
|
|
199
|
+
show_pause_menu "$task_completed" "$task_total" "$current_task_name" "$timeout"
|
|
200
|
+
return $?
|
|
201
|
+
;;
|
|
202
|
+
*)
|
|
203
|
+
# Unknown input, continue
|
|
204
|
+
TUI_LAST_ACTION="continue"
|
|
205
|
+
return 0
|
|
206
|
+
;;
|
|
207
|
+
esac
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
211
|
+
# Pause Screen
|
|
212
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
213
|
+
|
|
214
|
+
show_pause_screen() {
|
|
215
|
+
clear 2>/dev/null || true
|
|
216
|
+
|
|
217
|
+
echo ""
|
|
218
|
+
echo -e "${YELLOW}╔═══════════════════════════════════════════════════════════════════╗${RESET}"
|
|
219
|
+
echo -e "${YELLOW}║${RESET} ${YELLOW}║${RESET}"
|
|
220
|
+
echo -e "${YELLOW}║${RESET} ${BOLD}⏸️ MERLIN LOOP PAUSED${RESET} ${YELLOW}║${RESET}"
|
|
221
|
+
echo -e "${YELLOW}║${RESET} ${YELLOW}║${RESET}"
|
|
222
|
+
echo -e "${YELLOW}║${RESET} Your work has been saved. You can safely: ${YELLOW}║${RESET}"
|
|
223
|
+
echo -e "${YELLOW}║${RESET} • Close this terminal ${YELLOW}║${RESET}"
|
|
224
|
+
echo -e "${YELLOW}║${RESET} • Come back later ${YELLOW}║${RESET}"
|
|
225
|
+
echo -e "${YELLOW}║${RESET} • Resume with: ${CYAN}merlin-loop resume${RESET} ${YELLOW}║${RESET}"
|
|
226
|
+
echo -e "${YELLOW}║${RESET} ${YELLOW}║${RESET}"
|
|
227
|
+
echo -e "${YELLOW}╠═══════════════════════════════════════════════════════════════════╣${RESET}"
|
|
228
|
+
echo -e "${YELLOW}║${RESET} ${YELLOW}║${RESET}"
|
|
229
|
+
echo -e "${YELLOW}║${RESET} ${GREEN}[Enter]${RESET} Resume now ${RED}[q]${RESET} Quit completely ${YELLOW}║${RESET}"
|
|
230
|
+
echo -e "${YELLOW}║${RESET} ${YELLOW}║${RESET}"
|
|
231
|
+
echo -e "${YELLOW}╚═══════════════════════════════════════════════════════════════════╝${RESET}"
|
|
232
|
+
echo ""
|
|
233
|
+
|
|
234
|
+
# Save checkpoint before pausing
|
|
235
|
+
if type save_checkpoint &> /dev/null; then
|
|
236
|
+
save_checkpoint "Loop paused by user" "" "" "[]" "merlin-loop" 2>/dev/null || true
|
|
237
|
+
fi
|
|
238
|
+
|
|
239
|
+
read -n 1 input
|
|
240
|
+
case "$input" in
|
|
241
|
+
"q"|"Q")
|
|
242
|
+
echo ""
|
|
243
|
+
echo -e "${YELLOW}Exiting. Resume later with: ${CYAN}merlin-loop resume${RESET}"
|
|
244
|
+
exit 0
|
|
245
|
+
;;
|
|
246
|
+
*)
|
|
247
|
+
echo ""
|
|
248
|
+
echo -e "${GREEN}Resuming...${RESET}"
|
|
249
|
+
;;
|
|
250
|
+
esac
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
254
|
+
# Status Screen
|
|
255
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
256
|
+
|
|
257
|
+
show_status_screen() {
|
|
258
|
+
echo ""
|
|
259
|
+
echo -e "${CYAN}═══════════════════════════════════════════════════════════════════${RESET}"
|
|
260
|
+
echo -e "${BOLD}📊 LOOP STATUS${RESET}"
|
|
261
|
+
echo -e "${CYAN}═══════════════════════════════════════════════════════════════════${RESET}"
|
|
262
|
+
echo ""
|
|
263
|
+
|
|
264
|
+
# Read state file
|
|
265
|
+
if [ -f "$STATE_FILE" ] && command -v jq &> /dev/null; then
|
|
266
|
+
local iteration completed errors
|
|
267
|
+
iteration=$(jq -r '.iteration // 0' "$STATE_FILE" 2>/dev/null || echo "0")
|
|
268
|
+
completed=$(jq -r '.tasks_completed // 0' "$STATE_FILE" 2>/dev/null || echo "0")
|
|
269
|
+
errors=$(jq -r '.consecutive_errors // 0' "$STATE_FILE" 2>/dev/null || echo "0")
|
|
270
|
+
local phase=$(jq -r '.current_phase // "N/A"' "$STATE_FILE" 2>/dev/null || echo "N/A")
|
|
271
|
+
|
|
272
|
+
echo -e " ${CYAN}Iterations:${RESET} $iteration"
|
|
273
|
+
echo -e " ${CYAN}Tasks Completed:${RESET} $completed"
|
|
274
|
+
echo -e " ${CYAN}Current Phase:${RESET} $phase"
|
|
275
|
+
echo -e " ${CYAN}Errors:${RESET} $errors"
|
|
276
|
+
else
|
|
277
|
+
echo -e " ${DIM}State file not available${RESET}"
|
|
278
|
+
fi
|
|
279
|
+
|
|
280
|
+
# Sights status
|
|
281
|
+
echo ""
|
|
282
|
+
if type check_sights_connection &> /dev/null; then
|
|
283
|
+
local sights_status
|
|
284
|
+
sights_status=$(check_sights_connection 2>/dev/null || echo "unknown")
|
|
285
|
+
case "$sights_status" in
|
|
286
|
+
"connected")
|
|
287
|
+
echo -e " ${GREEN}🔮 Sights: Connected${RESET}"
|
|
288
|
+
;;
|
|
289
|
+
*)
|
|
290
|
+
echo -e " ${YELLOW}🔮 Sights: $sights_status${RESET}"
|
|
291
|
+
;;
|
|
292
|
+
esac
|
|
293
|
+
fi
|
|
294
|
+
|
|
295
|
+
# Git status
|
|
296
|
+
echo ""
|
|
297
|
+
local git_status
|
|
298
|
+
git_status=$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ')
|
|
299
|
+
if [ "$git_status" -eq 0 ]; then
|
|
300
|
+
echo -e " ${GREEN}📁 Git: Clean${RESET}"
|
|
301
|
+
else
|
|
302
|
+
echo -e " ${YELLOW}📁 Git: $git_status uncommitted changes${RESET}"
|
|
303
|
+
fi
|
|
304
|
+
|
|
305
|
+
echo ""
|
|
306
|
+
echo -e "${CYAN}═══════════════════════════════════════════════════════════════════${RESET}"
|
|
307
|
+
echo ""
|
|
308
|
+
echo -e "${DIM}Press any key to continue...${RESET}"
|
|
309
|
+
read -n 1
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
313
|
+
# Task Header
|
|
314
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
315
|
+
|
|
316
|
+
# Show task header when starting a new task
|
|
317
|
+
show_task_header() {
|
|
318
|
+
local task_num=${1:-1}
|
|
319
|
+
local task_total=${2:-1}
|
|
320
|
+
local task_name="${3:-Task}"
|
|
321
|
+
local agent_name="${4:-claude}"
|
|
322
|
+
|
|
323
|
+
echo ""
|
|
324
|
+
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
|
|
325
|
+
echo -e "${BOLD}▶️ TASK ${task_num}/${task_total}${RESET}: ${task_name}"
|
|
326
|
+
echo -e "${DIM}Agent: ${agent_name}${RESET}"
|
|
327
|
+
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
|
|
328
|
+
echo ""
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
# Show task completion
|
|
332
|
+
show_task_complete() {
|
|
333
|
+
local task_name="${1:-Task}"
|
|
334
|
+
local duration="${2:-0}"
|
|
335
|
+
local status="${3:-success}"
|
|
336
|
+
|
|
337
|
+
echo ""
|
|
338
|
+
|
|
339
|
+
if [ "$status" == "success" ]; then
|
|
340
|
+
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
|
|
341
|
+
echo -e "${GREEN}✅ COMPLETE:${RESET} ${task_name}"
|
|
342
|
+
echo -e "${DIM}Duration: ${duration}s${RESET}"
|
|
343
|
+
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
|
|
344
|
+
else
|
|
345
|
+
echo -e "${RED}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
|
|
346
|
+
echo -e "${RED}❌ FAILED:${RESET} ${task_name}"
|
|
347
|
+
echo -e "${DIM}Duration: ${duration}s${RESET}"
|
|
348
|
+
echo -e "${RED}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
|
|
349
|
+
fi
|
|
350
|
+
|
|
351
|
+
echo ""
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
355
|
+
# Loop Complete Banner
|
|
356
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
357
|
+
|
|
358
|
+
show_loop_complete() {
|
|
359
|
+
local tasks_completed=${1:-0}
|
|
360
|
+
local total_duration=${2:-0}
|
|
361
|
+
local errors=${3:-0}
|
|
362
|
+
|
|
363
|
+
echo ""
|
|
364
|
+
echo -e "${GREEN}╔═══════════════════════════════════════════════════════════════════╗${RESET}"
|
|
365
|
+
echo -e "${GREEN}║${RESET} ${GREEN}║${RESET}"
|
|
366
|
+
echo -e "${GREEN}║${RESET} ${BOLD}🎉 MERLIN LOOP COMPLETE!${RESET} ${GREEN}║${RESET}"
|
|
367
|
+
echo -e "${GREEN}║${RESET} ${GREEN}║${RESET}"
|
|
368
|
+
echo -e "${GREEN}╠═══════════════════════════════════════════════════════════════════╣${RESET}"
|
|
369
|
+
echo -e "${GREEN}║${RESET} ${GREEN}║${RESET}"
|
|
370
|
+
printf "${GREEN}║${RESET} ${CYAN}Tasks Completed:${RESET} %-45s ${GREEN}║${RESET}\n" "$tasks_completed"
|
|
371
|
+
printf "${GREEN}║${RESET} ${CYAN}Total Duration:${RESET} %-45s ${GREEN}║${RESET}\n" "${total_duration}s"
|
|
372
|
+
printf "${GREEN}║${RESET} ${CYAN}Errors:${RESET} %-45s ${GREEN}║${RESET}\n" "$errors"
|
|
373
|
+
echo -e "${GREEN}║${RESET} ${GREEN}║${RESET}"
|
|
374
|
+
echo -e "${GREEN}╚═══════════════════════════════════════════════════════════════════╝${RESET}"
|
|
375
|
+
echo ""
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
379
|
+
# Exports
|
|
380
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
381
|
+
|
|
382
|
+
export -f draw_progress_bar 2>/dev/null || true
|
|
383
|
+
export -f start_spinner 2>/dev/null || true
|
|
384
|
+
export -f stop_spinner 2>/dev/null || true
|
|
385
|
+
export -f show_pause_menu 2>/dev/null || true
|
|
386
|
+
export -f show_task_header 2>/dev/null || true
|
|
387
|
+
export -f show_task_complete 2>/dev/null || true
|
|
388
|
+
export -f show_loop_complete 2>/dev/null || true
|