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.
@@ -0,0 +1,453 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # Merlin Loop - Boot Sequence
4
+ # The 5-phase Merlin Pro boot sequence that runs before every task
5
+ # Part of Merlin Pro v1.0
6
+ #
7
+
8
+ # ═══════════════════════════════════════════════════════════════════════════════
9
+ # Boot Sequence State
10
+ # ═══════════════════════════════════════════════════════════════════════════════
11
+
12
+ BOOT_CONTEXT_TOKENS=0
13
+ BOOT_SELECTED_AGENT=""
14
+ BOOT_INSTALLED_SKILLS=""
15
+ BOOT_TRAJECTORY=""
16
+ BOOT_RECENT_CHANGES=""
17
+ BOOT_CHECKPOINT_SUMMARY=""
18
+ BOOT_HOT_FILES=""
19
+ BOOT_DECISION_COUNT=0
20
+
21
+ # Colors (ensure they're defined)
22
+ : "${RESET:=\033[0m}"
23
+ : "${BOLD:=\033[1m}"
24
+ : "${DIM:=\033[2m}"
25
+ : "${RED:=\033[31m}"
26
+ : "${GREEN:=\033[32m}"
27
+ : "${YELLOW:=\033[33m}"
28
+ : "${BLUE:=\033[34m}"
29
+ : "${MAGENTA:=\033[35m}"
30
+ : "${CYAN:=\033[36m}"
31
+
32
+ # Spinner frames for animated loading
33
+ BOOT_SPINNER=("⠋" "⠙" "⠹" "⠸" "⠼" "⠴" "⠦" "⠧" "⠇" "⠏")
34
+
35
+ # ═══════════════════════════════════════════════════════════════════════════════
36
+ # Boot Animation Helpers
37
+ # ═══════════════════════════════════════════════════════════════════════════════
38
+
39
+ # Animated phase indicator
40
+ boot_phase_start() {
41
+ local phase_num="$1"
42
+ local phase_name="$2"
43
+ printf "${CYAN}[%s/5]${RESET} ${BOLD}%s${RESET}" "$phase_num" "$phase_name"
44
+ }
45
+
46
+ # Phase success with checkmark
47
+ boot_phase_success() {
48
+ local message="$1"
49
+ echo -e "\r${GREEN} ✓${RESET} $message"
50
+ }
51
+
52
+ # Phase warning
53
+ boot_phase_warn() {
54
+ local message="$1"
55
+ echo -e "\r${YELLOW} ⚠${RESET} $message"
56
+ }
57
+
58
+ # Phase skip
59
+ boot_phase_skip() {
60
+ local message="$1"
61
+ echo -e "\r${DIM} ⏭ $message${RESET}"
62
+ }
63
+
64
+ # Quick spinner (single line update)
65
+ boot_spinner() {
66
+ local message="$1"
67
+ local i=0
68
+ printf "\r ${CYAN}%s${RESET} %s" "${BOOT_SPINNER[$i]}" "$message"
69
+ sleep 0.1
70
+ }
71
+
72
+ # ═══════════════════════════════════════════════════════════════════════════════
73
+ # Phase 1: Reinforce Sights
74
+ # ═══════════════════════════════════════════════════════════════════════════════
75
+
76
+ boot_phase_1_sights() {
77
+ local task_description="$1"
78
+
79
+ echo ""
80
+ boot_phase_start "1" "Reinforcing Sights..."
81
+ echo ""
82
+
83
+ # Check Sights connection
84
+ if type check_sights_connection &> /dev/null; then
85
+ local status
86
+ status=$(check_sights_connection 2>/dev/null || echo "not_configured")
87
+
88
+ if [ "$status" != "connected" ]; then
89
+ boot_phase_warn "Sights not connected - using local context"
90
+ return 1
91
+ fi
92
+ boot_phase_success "Sights: ${GREEN}Connected${RESET}"
93
+ fi
94
+
95
+ # Load checkpoint
96
+ if type fetch_checkpoint &> /dev/null; then
97
+ boot_spinner "Loading checkpoint..."
98
+ local checkpoint
99
+ checkpoint=$(fetch_checkpoint 2>/dev/null || echo "")
100
+ if [ -n "$checkpoint" ] && [ "$checkpoint" != "null" ]; then
101
+ BOOT_CHECKPOINT_SUMMARY=$(echo "$checkpoint" | jq -r '.checkpoint.summary // empty' 2>/dev/null || echo "")
102
+ local created_at
103
+ created_at=$(echo "$checkpoint" | jq -r '.checkpoint.createdAt // empty' 2>/dev/null | cut -c1-16 || echo "")
104
+ if [ -n "$BOOT_CHECKPOINT_SUMMARY" ]; then
105
+ boot_phase_success "Checkpoint: ${BOOT_CHECKPOINT_SUMMARY:0:50}"
106
+ BOOT_CONTEXT_TOKENS=$((BOOT_CONTEXT_TOKENS + 200))
107
+ else
108
+ boot_phase_skip "No checkpoint found"
109
+ fi
110
+ else
111
+ boot_phase_skip "No checkpoint found"
112
+ fi
113
+ fi
114
+
115
+ # Load trajectory
116
+ if type fetch_trajectory &> /dev/null; then
117
+ boot_spinner "Detecting trajectory..."
118
+ BOOT_TRAJECTORY=$(fetch_trajectory 2>/dev/null || echo "")
119
+ if [ -n "$BOOT_TRAJECTORY" ] && [ "$BOOT_TRAJECTORY" != "Not detected" ]; then
120
+ boot_phase_success "Trajectory: ${CYAN}\"$BOOT_TRAJECTORY\"${RESET}"
121
+ BOOT_CONTEXT_TOKENS=$((BOOT_CONTEXT_TOKENS + 50))
122
+ else
123
+ boot_phase_skip "Trajectory not detected yet"
124
+ fi
125
+ fi
126
+
127
+ # Load recent changes
128
+ if type fetch_recent_changes &> /dev/null; then
129
+ boot_spinner "Loading recent changes..."
130
+ BOOT_RECENT_CHANGES=$(fetch_recent_changes 2>/dev/null || echo "")
131
+ if [ -n "$BOOT_RECENT_CHANGES" ]; then
132
+ # Extract hot files
133
+ local hot_files
134
+ hot_files=$(echo "$BOOT_RECENT_CHANGES" | grep -o '\*\*[^*]*\*\*:' | head -3 | sed 's/\*\*//g' | sed 's/:$//' | tr '\n' ', ' | sed 's/,$//')
135
+ if [ -n "$hot_files" ]; then
136
+ BOOT_HOT_FILES="$hot_files"
137
+ boot_phase_success "Hot files: ${YELLOW}$hot_files${RESET}"
138
+ BOOT_CONTEXT_TOKENS=$((BOOT_CONTEXT_TOKENS + 300))
139
+ else
140
+ boot_phase_skip "No recent commits tracked"
141
+ fi
142
+ else
143
+ boot_phase_skip "Recent changes not available"
144
+ fi
145
+ fi
146
+
147
+ # Load recent decisions
148
+ if type get_decisions &> /dev/null; then
149
+ boot_spinner "Loading decisions..."
150
+ local decisions
151
+ decisions=$(get_decisions 5 2>/dev/null || echo "")
152
+ if [ -n "$decisions" ]; then
153
+ BOOT_DECISION_COUNT=$(echo "$decisions" | jq -r 'if type == "object" then (.count // (.decisions | length) // 0) else 0 end' 2>/dev/null || echo "0")
154
+ if [ "$BOOT_DECISION_COUNT" -gt 0 ]; then
155
+ boot_phase_success "Decisions: ${BOOT_DECISION_COUNT} recent decisions loaded"
156
+ BOOT_CONTEXT_TOKENS=$((BOOT_CONTEXT_TOKENS + 100))
157
+ else
158
+ boot_phase_skip "No decisions recorded yet"
159
+ fi
160
+ else
161
+ boot_phase_skip "Decisions not available"
162
+ fi
163
+ fi
164
+
165
+ return 0
166
+ }
167
+
168
+ # ═══════════════════════════════════════════════════════════════════════════════
169
+ # Phase 2: Analyze Task & Select Agent
170
+ # ═══════════════════════════════════════════════════════════════════════════════
171
+
172
+ boot_phase_2_agent() {
173
+ local task_description="$1"
174
+
175
+ echo ""
176
+ boot_phase_start "2" "Analyzing task..."
177
+ echo ""
178
+
179
+ # Select agent based on task keywords
180
+ if type select_agent_for_task &> /dev/null; then
181
+ BOOT_SELECTED_AGENT=$(select_agent_for_task "$task_description" 2>/dev/null || echo "implementation_dev")
182
+ else
183
+ BOOT_SELECTED_AGENT="implementation_dev"
184
+ fi
185
+
186
+ local agent_name
187
+ if type get_agent_display_name &> /dev/null; then
188
+ agent_name=$(get_agent_display_name "$BOOT_SELECTED_AGENT" 2>/dev/null || echo "$BOOT_SELECTED_AGENT")
189
+ else
190
+ agent_name="$BOOT_SELECTED_AGENT"
191
+ fi
192
+
193
+ boot_phase_success "Agent: ${BOLD}${MAGENTA}$agent_name${RESET}"
194
+
195
+ # Load agent-specific context
196
+ if type get_agent_context_injection &> /dev/null; then
197
+ local agent_context
198
+ agent_context=$(get_agent_context_injection "$BOOT_SELECTED_AGENT" 2>/dev/null || echo "")
199
+ if [ -n "$agent_context" ]; then
200
+ local context_lines
201
+ context_lines=$(echo "$agent_context" | wc -l | tr -d ' ')
202
+ boot_phase_success "Domain knowledge: ${context_lines} guidelines loaded"
203
+ BOOT_CONTEXT_TOKENS=$((BOOT_CONTEXT_TOKENS + 150))
204
+ fi
205
+ fi
206
+
207
+ return 0
208
+ }
209
+
210
+ # ═══════════════════════════════════════════════════════════════════════════════
211
+ # Phase 3: Scan Skills Index
212
+ # ═══════════════════════════════════════════════════════════════════════════════
213
+
214
+ boot_phase_3_scan_skills() {
215
+ local task_description="$1"
216
+
217
+ echo ""
218
+ boot_phase_start "3" "Scanning skills index..."
219
+ echo ""
220
+
221
+ # Get required skills for selected agent
222
+ local required_skills=""
223
+ if type get_agent_required_skills &> /dev/null; then
224
+ required_skills=$(get_agent_required_skills "$BOOT_SELECTED_AGENT" 2>/dev/null || echo "")
225
+ fi
226
+
227
+ # Detect additional skills from task keywords
228
+ local detected_skills=""
229
+ if type detect_skills_from_task &> /dev/null; then
230
+ detected_skills=$(detect_skills_from_task "$task_description" 2>/dev/null || echo "")
231
+ fi
232
+
233
+ # Combine and deduplicate
234
+ local all_skills
235
+ all_skills=$(echo -e "$required_skills\n$detected_skills" | sort -u | grep -v "^$" || echo "")
236
+
237
+ if [ -n "$all_skills" ]; then
238
+ local skill_count
239
+ skill_count=$(echo "$all_skills" | wc -l | tr -d ' ')
240
+ boot_phase_success "Found ${skill_count} relevant skill(s)"
241
+
242
+ # Check which are installed
243
+ local installed=""
244
+ if type get_installed_skills &> /dev/null; then
245
+ installed=$(get_installed_skills 2>/dev/null || echo "")
246
+ fi
247
+
248
+ # Store for phase 4
249
+ BOOT_NEEDED_SKILLS="$all_skills"
250
+ BOOT_INSTALLED_SKILLS="$installed"
251
+ else
252
+ boot_phase_success "No additional skills needed"
253
+ fi
254
+
255
+ return 0
256
+ }
257
+
258
+ # ═══════════════════════════════════════════════════════════════════════════════
259
+ # Phase 4: Install Missing Skills
260
+ # ═══════════════════════════════════════════════════════════════════════════════
261
+
262
+ boot_phase_4_install_skills() {
263
+ echo ""
264
+ boot_phase_start "4" "Checking skills..."
265
+ echo ""
266
+
267
+ if [ -z "$BOOT_NEEDED_SKILLS" ]; then
268
+ boot_phase_success "All skills ready"
269
+ return 0
270
+ fi
271
+
272
+ local installed_count=0
273
+ local skipped_count=0
274
+
275
+ while IFS= read -r skill; do
276
+ [ -z "$skill" ] && continue
277
+
278
+ if echo "$BOOT_INSTALLED_SKILLS" | grep -q "^${skill}$"; then
279
+ ((skipped_count++))
280
+ else
281
+ # Install the skill
282
+ if type install_skill &> /dev/null && install_skill "$skill" 2>/dev/null; then
283
+ boot_phase_success "Installed: ${GREEN}$skill${RESET}"
284
+ ((installed_count++))
285
+ BOOT_CONTEXT_TOKENS=$((BOOT_CONTEXT_TOKENS + 500))
286
+ else
287
+ boot_phase_skip "$skill (not available)"
288
+ ((skipped_count++))
289
+ fi
290
+ fi
291
+ done <<< "$BOOT_NEEDED_SKILLS"
292
+
293
+ if [ $installed_count -eq 0 ]; then
294
+ boot_phase_success "All ${skipped_count} skill(s) ready"
295
+ fi
296
+
297
+ return 0
298
+ }
299
+
300
+ # ═══════════════════════════════════════════════════════════════════════════════
301
+ # Phase 5: Start Work
302
+ # ═══════════════════════════════════════════════════════════════════════════════
303
+
304
+ boot_phase_5_ready() {
305
+ local task_description="$1"
306
+
307
+ echo ""
308
+ boot_phase_start "5" "Boot complete!"
309
+ echo ""
310
+
311
+ # Show summary box
312
+ echo -e " ${CYAN}┌──────────────────────────────────────────────────────┐${RESET}"
313
+ printf " ${CYAN}│${RESET} %-52s ${CYAN}│${RESET}\n" "Context: ~${BOOT_CONTEXT_TOKENS} tokens loaded"
314
+
315
+ local agent_display
316
+ if type get_agent_display_name &> /dev/null; then
317
+ agent_display=$(get_agent_display_name "$BOOT_SELECTED_AGENT" 2>/dev/null || echo "$BOOT_SELECTED_AGENT")
318
+ else
319
+ agent_display="$BOOT_SELECTED_AGENT"
320
+ fi
321
+ printf " ${CYAN}│${RESET} %-52s ${CYAN}│${RESET}\n" "Agent: $agent_display"
322
+
323
+ if [ -n "$BOOT_TRAJECTORY" ] && [ "$BOOT_TRAJECTORY" != "Not detected" ]; then
324
+ printf " ${CYAN}│${RESET} %-52s ${CYAN}│${RESET}\n" "Trajectory: ${BOOT_TRAJECTORY:0:40}"
325
+ fi
326
+
327
+ if [ -n "$BOOT_HOT_FILES" ]; then
328
+ printf " ${CYAN}│${RESET} %-52s ${CYAN}│${RESET}\n" "Hot files: ${BOOT_HOT_FILES:0:40}"
329
+ fi
330
+
331
+ if [ "$BOOT_DECISION_COUNT" -gt 0 ]; then
332
+ printf " ${CYAN}│${RESET} %-52s ${CYAN}│${RESET}\n" "Decisions: $BOOT_DECISION_COUNT loaded"
333
+ fi
334
+
335
+ echo -e " ${CYAN}└──────────────────────────────────────────────────────┘${RESET}"
336
+
337
+ return 0
338
+ }
339
+
340
+ # ═══════════════════════════════════════════════════════════════════════════════
341
+ # Main Boot Sequence
342
+ # ═══════════════════════════════════════════════════════════════════════════════
343
+
344
+ run_boot_sequence() {
345
+ local task_description="${1:-Unknown task}"
346
+ local quiet="${2:-false}"
347
+
348
+ # Reset state
349
+ BOOT_CONTEXT_TOKENS=0
350
+ BOOT_SELECTED_AGENT=""
351
+ BOOT_INSTALLED_SKILLS=""
352
+ BOOT_TRAJECTORY=""
353
+ BOOT_RECENT_CHANGES=""
354
+ BOOT_NEEDED_SKILLS=""
355
+ BOOT_CHECKPOINT_SUMMARY=""
356
+ BOOT_HOT_FILES=""
357
+ BOOT_DECISION_COUNT=0
358
+
359
+ if [ "$quiet" != "true" ]; then
360
+ echo ""
361
+ echo -e "${MAGENTA}╔═══════════════════════════════════════════════════════════════════╗${RESET}"
362
+ echo -e "${MAGENTA}║${RESET} ${BOLD}🚀 MERLIN PRO BOOT SEQUENCE${RESET} ${MAGENTA}║${RESET}"
363
+ echo -e "${MAGENTA}╚═══════════════════════════════════════════════════════════════════╝${RESET}"
364
+ fi
365
+
366
+ # Run all 5 phases
367
+ boot_phase_1_sights "$task_description"
368
+ boot_phase_2_agent "$task_description"
369
+ boot_phase_3_scan_skills "$task_description"
370
+ boot_phase_4_install_skills
371
+ boot_phase_5_ready "$task_description"
372
+
373
+ if [ "$quiet" != "true" ]; then
374
+ echo ""
375
+ echo -e "${GREEN}╔═══════════════════════════════════════════════════════════════════╗${RESET}"
376
+ printf "${GREEN}║${RESET} %-65s ${GREEN}║${RESET}\n" "▶️ Starting: ${task_description:0:50}"
377
+ echo -e "${GREEN}╚═══════════════════════════════════════════════════════════════════╝${RESET}"
378
+ echo ""
379
+ fi
380
+
381
+ return 0
382
+ }
383
+
384
+ # ═══════════════════════════════════════════════════════════════════════════════
385
+ # Quick Boot (minimal output)
386
+ # ═══════════════════════════════════════════════════════════════════════════════
387
+
388
+ run_boot_quick() {
389
+ local task_description="${1:-Unknown task}"
390
+
391
+ # Reset state
392
+ BOOT_CONTEXT_TOKENS=0
393
+ BOOT_SELECTED_AGENT=""
394
+ BOOT_TRAJECTORY=""
395
+
396
+ # Quick loads (no output)
397
+ if type fetch_trajectory &> /dev/null; then
398
+ BOOT_TRAJECTORY=$(fetch_trajectory 2>/dev/null || echo "")
399
+ fi
400
+
401
+ if type select_agent_for_task &> /dev/null; then
402
+ BOOT_SELECTED_AGENT=$(select_agent_for_task "$task_description" 2>/dev/null || echo "implementation_dev")
403
+ fi
404
+
405
+ BOOT_CONTEXT_TOKENS=500 # Estimate
406
+
407
+ echo -e "${GREEN}🚀${RESET} Boot complete (quick mode) - Agent: $BOOT_SELECTED_AGENT"
408
+ }
409
+
410
+ # ═══════════════════════════════════════════════════════════════════════════════
411
+ # Helper: Get boot context for prompt injection
412
+ # ═══════════════════════════════════════════════════════════════════════════════
413
+
414
+ get_boot_context() {
415
+ local context=""
416
+
417
+ # Add trajectory
418
+ if [ -n "$BOOT_TRAJECTORY" ] && [ "$BOOT_TRAJECTORY" != "Not detected" ]; then
419
+ context="**Current Trajectory:** $BOOT_TRAJECTORY
420
+
421
+ "
422
+ fi
423
+
424
+ # Add recent changes
425
+ if [ -n "$BOOT_RECENT_CHANGES" ]; then
426
+ context="${context}${BOOT_RECENT_CHANGES}
427
+
428
+ "
429
+ fi
430
+
431
+ # Add agent context
432
+ if [ -n "$BOOT_SELECTED_AGENT" ] && type get_agent_context_injection &> /dev/null; then
433
+ local agent_context
434
+ agent_context=$(get_agent_context_injection "$BOOT_SELECTED_AGENT" 2>/dev/null || echo "")
435
+ if [ -n "$agent_context" ]; then
436
+ context="${context}## Agent Guidance ($BOOT_SELECTED_AGENT)
437
+
438
+ $agent_context
439
+
440
+ "
441
+ fi
442
+ fi
443
+
444
+ echo "$context"
445
+ }
446
+
447
+ # ═══════════════════════════════════════════════════════════════════════════════
448
+ # Exports
449
+ # ═══════════════════════════════════════════════════════════════════════════════
450
+
451
+ export -f run_boot_sequence 2>/dev/null || true
452
+ export -f run_boot_quick 2>/dev/null || true
453
+ export -f get_boot_context 2>/dev/null || true
@@ -0,0 +1,224 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # Merlin Loop - Discussion Mode
4
+ # Spawns a fresh Claude session for conversation without losing loop state
5
+ #
6
+
7
+ # ═══════════════════════════════════════════════════════════════════════════════
8
+ # Discussion State
9
+ # ═══════════════════════════════════════════════════════════════════════════════
10
+
11
+ DISCUSSION_DECISIONS=()
12
+ DISCUSSION_CONTEXT=""
13
+
14
+ # ═══════════════════════════════════════════════════════════════════════════════
15
+ # Context Building
16
+ # ═══════════════════════════════════════════════════════════════════════════════
17
+
18
+ # Build a minimal context summary for discussion mode
19
+ # Keeps under 2000 tokens for lightweight conversation
20
+ build_discussion_context() {
21
+ local output="$1"
22
+ local iteration="$2"
23
+
24
+ local context=""
25
+
26
+ # Current task/situation
27
+ context+="## Current Situation\n\n"
28
+ context+="Loop iteration: $iteration\n"
29
+
30
+ # Extract what we're working on from the output
31
+ if echo "$output" | grep -q "DECISION_NEEDED"; then
32
+ local decision_line
33
+ decision_line=$(echo "$output" | grep -o "DECISION_NEEDED|[^|]*|[^|]*" | head -1)
34
+ local reason
35
+ reason=$(echo "$decision_line" | cut -d'|' -f2)
36
+ local details
37
+ details=$(echo "$decision_line" | cut -d'|' -f3)
38
+ context+="Decision needed: $reason\n"
39
+ context+="Details: $details\n"
40
+ elif echo "$output" | grep -q "LOW_CONFIDENCE"; then
41
+ local confidence_line
42
+ confidence_line=$(echo "$output" | grep -o "LOW_CONFIDENCE|[^|]*|[^|]*" | head -1)
43
+ local reason
44
+ reason=$(echo "$confidence_line" | cut -d'|' -f2)
45
+ local details
46
+ details=$(echo "$confidence_line" | cut -d'|' -f3)
47
+ context+="Uncertainty: $reason\n"
48
+ context+="Details: $details\n"
49
+ fi
50
+ context+="\n"
51
+
52
+ # Recent history (last 3 tasks)
53
+ context+="## Recent Progress\n\n"
54
+ if [ -f "$HISTORY_FILE" ]; then
55
+ local recent_tasks
56
+ recent_tasks=$(grep '"type":"task_complete"' "$HISTORY_FILE" | tail -3)
57
+ if [ -n "$recent_tasks" ]; then
58
+ echo "$recent_tasks" | while read -r line; do
59
+ if command -v jq &> /dev/null; then
60
+ local task_name
61
+ task_name=$(echo "$line" | jq -r '.task_name // "Unknown task"')
62
+ context+="- ✓ $task_name\n"
63
+ fi
64
+ done
65
+ else
66
+ context+="- No tasks completed yet\n"
67
+ fi
68
+ fi
69
+ context+="\n"
70
+
71
+ # Any pending decisions from previous discussions
72
+ if [ ${#DISCUSSION_DECISIONS[@]} -gt 0 ]; then
73
+ context+="## Previous Decisions (this session)\n\n"
74
+ for decision in "${DISCUSSION_DECISIONS[@]}"; do
75
+ context+="- $decision\n"
76
+ done
77
+ context+="\n"
78
+ fi
79
+
80
+ # Project summary if available
81
+ if [ -f ".planning/PROJECT.md" ]; then
82
+ context+="## Project\n\n"
83
+ context+=$(head -20 ".planning/PROJECT.md" | grep -v "^#" | head -5)
84
+ context+="\n"
85
+ fi
86
+
87
+ DISCUSSION_CONTEXT="$context"
88
+ echo -e "$context"
89
+ }
90
+
91
+ # ═══════════════════════════════════════════════════════════════════════════════
92
+ # Discussion Spawning
93
+ # ═══════════════════════════════════════════════════════════════════════════════
94
+
95
+ # Spawn a fresh Claude session for discussion
96
+ # The user can chat, think through problems, and make decisions
97
+ spawn_discussion() {
98
+ local output="$1"
99
+ local iteration="$2"
100
+
101
+ echo ""
102
+ echo -e "${CYAN}╔═══════════════════════════════════════════════════════════════╗${RESET}"
103
+ echo -e "${CYAN}║ ${BOLD}DISCUSSION MODE${RESET}${CYAN} ║${RESET}"
104
+ echo -e "${CYAN}║ Fresh Claude for conversation (no code changes) ║${RESET}"
105
+ echo -e "${CYAN}╚═══════════════════════════════════════════════════════════════╝${RESET}"
106
+ echo ""
107
+
108
+ # Build context
109
+ local context
110
+ context=$(build_discussion_context "$output" "$iteration")
111
+
112
+ # Load discussion prompt
113
+ local prompt_file="$SCRIPT_DIR/prompts/PROMPT_DISCUSS.md"
114
+ if [ ! -f "$prompt_file" ]; then
115
+ echo -e "${YELLOW}Discussion prompt not found, using default${RESET}"
116
+ prompt_file=""
117
+ fi
118
+
119
+ # Build the full prompt
120
+ local full_prompt=""
121
+ if [ -n "$prompt_file" ]; then
122
+ full_prompt=$(cat "$prompt_file")
123
+ else
124
+ full_prompt="You are in DISCUSSION MODE - an advisor helping think through problems.
125
+ Do NOT make code changes. Help the user think, ask questions, suggest approaches.
126
+ When the user makes a decision, mark it with DECISION: prefix.
127
+ When done, the user will say 'done' or 'continue'."
128
+ fi
129
+
130
+ # Inject context
131
+ full_prompt=$(echo "$full_prompt" | sed "s/{{CURRENT_TASK}}/$(echo "$context" | head -5 | tr '\n' ' ')/g")
132
+ full_prompt=$(echo "$full_prompt" | sed "s/{{RECENT_HISTORY}}/See context below/g")
133
+ full_prompt=$(echo "$full_prompt" | sed "s/{{PENDING_DECISIONS}}/None/g")
134
+ full_prompt=$(echo "$full_prompt" | sed "s/{{PROJECT_CONTEXT}}/See context below/g")
135
+
136
+ full_prompt+="
137
+
138
+ ---
139
+ ## Context
140
+
141
+ $context
142
+ ---
143
+
144
+ I'm ready to discuss. What's on your mind?"
145
+
146
+ echo -e "${BLUE}Starting discussion session...${RESET}"
147
+ echo -e "${BLUE}Type 'done' or 'continue' when ready to return to the loop.${RESET}"
148
+ echo ""
149
+
150
+ # Spawn Claude in interactive mode (not stream-json)
151
+ local discussion_output
152
+ local ai_cli
153
+ ai_cli=$(detect_ai_cli 2>/dev/null || echo "claude")
154
+
155
+ # Run interactively - this blocks until user ends discussion
156
+ case "$ai_cli" in
157
+ claude)
158
+ # Use interactive mode without --output-format
159
+ discussion_output=$(echo "$full_prompt" | claude 2>&1)
160
+ ;;
161
+ *)
162
+ discussion_output=$(echo "$full_prompt" | "$ai_cli" 2>&1)
163
+ ;;
164
+ esac
165
+
166
+ # Process decisions from the discussion
167
+ save_discussion_decisions "$discussion_output"
168
+
169
+ echo ""
170
+ echo -e "${GREEN}Discussion complete.${RESET}"
171
+
172
+ local decision_count=${#DISCUSSION_DECISIONS[@]}
173
+ if [ "$decision_count" -gt 0 ]; then
174
+ echo -e "${GREEN}Captured $decision_count decision(s) for future context.${RESET}"
175
+ fi
176
+
177
+ echo ""
178
+ }
179
+
180
+ # ═══════════════════════════════════════════════════════════════════════════════
181
+ # Decision Capture
182
+ # ═══════════════════════════════════════════════════════════════════════════════
183
+
184
+ # Parse Claude's output for DECISION: markers and save them
185
+ save_discussion_decisions() {
186
+ local output="$1"
187
+
188
+ # Extract all DECISION: lines
189
+ while IFS= read -r line; do
190
+ local decision
191
+ decision=$(echo "$line" | sed 's/DECISION://')
192
+ decision=$(echo "$decision" | xargs) # Trim whitespace
193
+
194
+ if [ -n "$decision" ]; then
195
+ DISCUSSION_DECISIONS+=("$decision")
196
+
197
+ # Also save to state file for persistence
198
+ if [ -f "$STATE_FILE" ] && command -v jq &> /dev/null; then
199
+ update_state_json ".memory.decisions += [\"[Discussion] $decision\"]"
200
+ fi
201
+
202
+ # Log to history
203
+ local now
204
+ now=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
205
+ echo "{\"type\":\"discussion_decision\",\"decision\":\"$decision\",\"timestamp\":\"$now\"}" >> "$HISTORY_FILE"
206
+ fi
207
+ done < <(echo "$output" | grep "^DECISION:" || echo "$output" | grep "DECISION:")
208
+ }
209
+
210
+ # Get decisions made in discussions (for injection into next task)
211
+ get_discussion_decisions() {
212
+ if [ ${#DISCUSSION_DECISIONS[@]} -gt 0 ]; then
213
+ echo "## Decisions from Discussion"
214
+ echo ""
215
+ for decision in "${DISCUSSION_DECISIONS[@]}"; do
216
+ echo "- $decision"
217
+ done
218
+ fi
219
+ }
220
+
221
+ # Clear discussion decisions (after they've been applied)
222
+ clear_discussion_decisions() {
223
+ DISCUSSION_DECISIONS=()
224
+ }