@the-bearded-bear/claude-craft 3.0.2 → 3.1.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.
Files changed (30) hide show
  1. package/Dev/i18n/de/Common/agents/ralph-conductor.md +146 -0
  2. package/Dev/i18n/de/Common/commands/ralph-run.md +171 -0
  3. package/Dev/i18n/de/Common/commands/setup-project-context.md +286 -0
  4. package/Dev/i18n/en/Common/agents/ralph-conductor.md +146 -0
  5. package/Dev/i18n/en/Common/commands/ralph-run.md +171 -0
  6. package/Dev/i18n/en/Common/commands/setup-project-context.md +286 -0
  7. package/Dev/i18n/es/Common/agents/ralph-conductor.md +146 -0
  8. package/Dev/i18n/es/Common/commands/ralph-run.md +171 -0
  9. package/Dev/i18n/es/Common/commands/setup-project-context.md +286 -0
  10. package/Dev/i18n/fr/Common/agents/ralph-conductor.md +146 -0
  11. package/Dev/i18n/fr/Common/commands/ralph-run.md +171 -0
  12. package/Dev/i18n/fr/Common/commands/setup-project-context.md +286 -0
  13. package/Dev/i18n/pt/Common/agents/ralph-conductor.md +146 -0
  14. package/Dev/i18n/pt/Common/commands/ralph-run.md +171 -0
  15. package/Dev/i18n/pt/Common/commands/setup-project-context.md +286 -0
  16. package/Tools/Ralph/README.md +303 -0
  17. package/Tools/Ralph/lib/checkpoint.sh +238 -0
  18. package/Tools/Ralph/lib/circuit-breaker.sh +172 -0
  19. package/Tools/Ralph/lib/dod-validator.sh +306 -0
  20. package/Tools/Ralph/lib/loop.sh +232 -0
  21. package/Tools/Ralph/lib/session.sh +234 -0
  22. package/Tools/Ralph/ralph.sh +491 -0
  23. package/Tools/Ralph/templates/ralph.yml.template +178 -0
  24. package/Tools/i18n/ralph/de.sh +147 -0
  25. package/Tools/i18n/ralph/en.sh +147 -0
  26. package/Tools/i18n/ralph/es.sh +147 -0
  27. package/Tools/i18n/ralph/fr.sh +147 -0
  28. package/Tools/i18n/ralph/pt.sh +147 -0
  29. package/cli/index.js +90 -0
  30. package/package.json +1 -1
@@ -0,0 +1,172 @@
1
+ #!/bin/bash
2
+ # =============================================================================
3
+ # Ralph Wiggum - Circuit Breaker Module
4
+ # Safety mechanism to prevent infinite loops and detect stalls
5
+ # =============================================================================
6
+
7
+ # Circuit breaker thresholds (defaults)
8
+ CB_NO_CHANGES_THRESHOLD="${CB_NO_CHANGES_THRESHOLD:-3}"
9
+ CB_REPEATED_ERROR_THRESHOLD="${CB_REPEATED_ERROR_THRESHOLD:-5}"
10
+ CB_OUTPUT_DECLINE_THRESHOLD="${CB_OUTPUT_DECLINE_THRESHOLD:-70}"
11
+
12
+ # Circuit breaker state
13
+ CB_ITERATIONS_WITHOUT_CHANGES=0
14
+ CB_CONSECUTIVE_ERRORS=0
15
+ CB_PEAK_OUTPUT_LENGTH=0
16
+ CB_LAST_OUTPUT_LENGTH=0
17
+ CB_TRIGGERED=false
18
+ CB_TRIGGER_REASON=""
19
+
20
+ # =============================================================================
21
+ # Initialization
22
+ # =============================================================================
23
+
24
+ init_circuit_breaker() {
25
+ CB_ITERATIONS_WITHOUT_CHANGES=0
26
+ CB_CONSECUTIVE_ERRORS=0
27
+ CB_PEAK_OUTPUT_LENGTH=0
28
+ CB_LAST_OUTPUT_LENGTH=0
29
+ CB_TRIGGERED=false
30
+ CB_TRIGGER_REASON=""
31
+
32
+ # Load thresholds from config if available
33
+ if [[ -n "$CONFIG_FILE" && -f "$CONFIG_FILE" ]] && command -v yq &> /dev/null; then
34
+ local no_changes=$(yq e '.circuit_breaker.no_file_changes_threshold // ""' "$CONFIG_FILE" 2>/dev/null)
35
+ [[ -n "$no_changes" ]] && CB_NO_CHANGES_THRESHOLD=$no_changes
36
+
37
+ local errors=$(yq e '.circuit_breaker.repeated_error_threshold // ""' "$CONFIG_FILE" 2>/dev/null)
38
+ [[ -n "$errors" ]] && CB_REPEATED_ERROR_THRESHOLD=$errors
39
+
40
+ local decline=$(yq e '.circuit_breaker.output_decline_threshold // ""' "$CONFIG_FILE" 2>/dev/null)
41
+ [[ -n "$decline" ]] && CB_OUTPUT_DECLINE_THRESHOLD=$decline
42
+ fi
43
+
44
+ print_verbose "Circuit breaker initialized:"
45
+ print_verbose " - No changes threshold: $CB_NO_CHANGES_THRESHOLD"
46
+ print_verbose " - Error threshold: $CB_REPEATED_ERROR_THRESHOLD"
47
+ print_verbose " - Output decline threshold: ${CB_OUTPUT_DECLINE_THRESHOLD}%"
48
+ }
49
+
50
+ # =============================================================================
51
+ # Circuit Breaker Check
52
+ # =============================================================================
53
+
54
+ check_circuit_breaker() {
55
+ # Return 0 (true) if triggered, 1 (false) if OK to continue
56
+
57
+ # Check no progress (no file changes)
58
+ if [[ $CB_ITERATIONS_WITHOUT_CHANGES -ge $CB_NO_CHANGES_THRESHOLD ]]; then
59
+ CB_TRIGGERED=true
60
+ CB_TRIGGER_REASON="no_progress"
61
+ print_warning "${MSG_CB_TRIGGERED}: ${MSG_CB_NO_CHANGES} $CB_ITERATIONS_WITHOUT_CHANGES ${MSG_CB_ITERATIONS}"
62
+ return 0
63
+ fi
64
+
65
+ # Check repeated errors
66
+ if [[ $CB_CONSECUTIVE_ERRORS -ge $CB_REPEATED_ERROR_THRESHOLD ]]; then
67
+ CB_TRIGGERED=true
68
+ CB_TRIGGER_REASON="repeated_errors"
69
+ print_warning "${MSG_CB_TRIGGERED}: ${MSG_CB_REPEATED_ERRORS}"
70
+ return 0
71
+ fi
72
+
73
+ # Check output decline
74
+ if [[ $CB_PEAK_OUTPUT_LENGTH -gt 0 && $CB_LAST_OUTPUT_LENGTH -gt 0 ]]; then
75
+ local decline_percent=$(( (CB_PEAK_OUTPUT_LENGTH - CB_LAST_OUTPUT_LENGTH) * 100 / CB_PEAK_OUTPUT_LENGTH ))
76
+ if [[ $decline_percent -ge $CB_OUTPUT_DECLINE_THRESHOLD ]]; then
77
+ CB_TRIGGERED=true
78
+ CB_TRIGGER_REASON="output_decline"
79
+ print_warning "${MSG_CB_TRIGGERED}: ${MSG_CB_OUTPUT_DECLINE} ${decline_percent}${MSG_CB_PERCENT}"
80
+ return 0
81
+ fi
82
+ fi
83
+
84
+ return 1
85
+ }
86
+
87
+ # =============================================================================
88
+ # Update Circuit Breaker State
89
+ # =============================================================================
90
+
91
+ update_circuit_breaker() {
92
+ local event_type="$1"
93
+ local output="$2"
94
+
95
+ case "$event_type" in
96
+ progress)
97
+ # Reset counters on progress
98
+ CB_ITERATIONS_WITHOUT_CHANGES=0
99
+ CB_CONSECUTIVE_ERRORS=0
100
+
101
+ # Update output tracking
102
+ local output_length=${#output}
103
+ CB_LAST_OUTPUT_LENGTH=$output_length
104
+ if [[ $output_length -gt $CB_PEAK_OUTPUT_LENGTH ]]; then
105
+ CB_PEAK_OUTPUT_LENGTH=$output_length
106
+ fi
107
+ ;;
108
+
109
+ no_progress)
110
+ CB_ITERATIONS_WITHOUT_CHANGES=$((CB_ITERATIONS_WITHOUT_CHANGES + 1))
111
+ print_verbose "No progress: $CB_ITERATIONS_WITHOUT_CHANGES / $CB_NO_CHANGES_THRESHOLD"
112
+ ;;
113
+
114
+ error)
115
+ CB_CONSECUTIVE_ERRORS=$((CB_CONSECUTIVE_ERRORS + 1))
116
+ print_verbose "Error count: $CB_CONSECUTIVE_ERRORS / $CB_REPEATED_ERROR_THRESHOLD"
117
+ ;;
118
+
119
+ reset)
120
+ CB_ITERATIONS_WITHOUT_CHANGES=0
121
+ CB_CONSECUTIVE_ERRORS=0
122
+ CB_TRIGGERED=false
123
+ CB_TRIGGER_REASON=""
124
+ print_verbose "${MSG_CB_RESET}"
125
+ ;;
126
+ esac
127
+
128
+ # Update session state if available
129
+ if [[ -n "$SESSION_ID" ]]; then
130
+ update_session_state "$SESSION_ID" "circuit_breaker" "{
131
+ \"iterations_without_changes\": $CB_ITERATIONS_WITHOUT_CHANGES,
132
+ \"consecutive_errors\": $CB_CONSECUTIVE_ERRORS,
133
+ \"peak_output_length\": $CB_PEAK_OUTPUT_LENGTH,
134
+ \"last_output_length\": $CB_LAST_OUTPUT_LENGTH,
135
+ \"triggered\": $CB_TRIGGERED,
136
+ \"trigger_reason\": \"$CB_TRIGGER_REASON\"
137
+ }"
138
+ fi
139
+ }
140
+
141
+ # =============================================================================
142
+ # Circuit Breaker Status
143
+ # =============================================================================
144
+
145
+ get_circuit_breaker_status() {
146
+ cat <<EOF
147
+ {
148
+ "iterations_without_changes": $CB_ITERATIONS_WITHOUT_CHANGES,
149
+ "consecutive_errors": $CB_CONSECUTIVE_ERRORS,
150
+ "peak_output_length": $CB_PEAK_OUTPUT_LENGTH,
151
+ "last_output_length": $CB_LAST_OUTPUT_LENGTH,
152
+ "triggered": $CB_TRIGGERED,
153
+ "trigger_reason": "$CB_TRIGGER_REASON",
154
+ "thresholds": {
155
+ "no_changes": $CB_NO_CHANGES_THRESHOLD,
156
+ "errors": $CB_REPEATED_ERROR_THRESHOLD,
157
+ "output_decline": $CB_OUTPUT_DECLINE_THRESHOLD
158
+ }
159
+ }
160
+ EOF
161
+ }
162
+
163
+ # =============================================================================
164
+ # Force Trip
165
+ # =============================================================================
166
+
167
+ trip_circuit_breaker() {
168
+ local reason="$1"
169
+ CB_TRIGGERED=true
170
+ CB_TRIGGER_REASON="${reason:-manual}"
171
+ print_warning "${MSG_CB_TRIGGERED}: $CB_TRIGGER_REASON"
172
+ }
@@ -0,0 +1,306 @@
1
+ #!/bin/bash
2
+ # =============================================================================
3
+ # Ralph Wiggum - Definition of Done Validator Module
4
+ # Validates completion criteria using configurable checklist
5
+ # =============================================================================
6
+
7
+ # Default completion marker
8
+ DOD_COMPLETION_MARKER="${DOD_COMPLETION_MARKER:-<promise>COMPLETE</promise>}"
9
+
10
+ # DoD validator types
11
+ declare -A DOD_VALIDATORS=(
12
+ ["command"]="validate_command"
13
+ ["output_contains"]="validate_output_contains"
14
+ ["file_changed"]="validate_file_changed"
15
+ ["hook"]="validate_hook"
16
+ ["human"]="validate_human"
17
+ )
18
+
19
+ # =============================================================================
20
+ # Main DoD Validation
21
+ # =============================================================================
22
+
23
+ validate_dod() {
24
+ local output="$1"
25
+ local config_file="$2"
26
+
27
+ # If no config, use simple completion marker check
28
+ if [[ -z "$config_file" || ! -f "$config_file" ]]; then
29
+ return $(validate_simple_completion "$output")
30
+ fi
31
+
32
+ # Check if yq is available for YAML parsing
33
+ if ! command -v yq &> /dev/null; then
34
+ return $(validate_simple_completion "$output")
35
+ fi
36
+
37
+ # Get DoD checklist from config
38
+ local checklist=$(yq e '.definition_of_done.checklist // []' "$config_file" 2>/dev/null)
39
+
40
+ # If no checklist, use simple completion marker
41
+ if [[ "$checklist" == "[]" || -z "$checklist" ]]; then
42
+ local marker=$(yq e '.definition_of_done.completion_marker // ""' "$config_file" 2>/dev/null)
43
+ if [[ -n "$marker" ]]; then
44
+ DOD_COMPLETION_MARKER="$marker"
45
+ fi
46
+ return $(validate_simple_completion "$output")
47
+ fi
48
+
49
+ # Process checklist items
50
+ local all_required_passed=true
51
+ local results=()
52
+
53
+ # Get count of checklist items
54
+ local count=$(echo "$checklist" | yq e 'length' -)
55
+
56
+ for ((i=0; i<count; i++)); do
57
+ local item=$(yq e ".definition_of_done.checklist[$i]" "$config_file")
58
+ local id=$(echo "$item" | yq e '.id' -)
59
+ local name=$(echo "$item" | yq e '.name' -)
60
+ local type=$(echo "$item" | yq e '.type' -)
61
+ local required=$(echo "$item" | yq e '.required // true' -)
62
+
63
+ # Validate based on type
64
+ local passed=false
65
+ local message=""
66
+
67
+ case "$type" in
68
+ command)
69
+ local cmd=$(echo "$item" | yq e '.command' -)
70
+ if validate_command "$cmd"; then
71
+ passed=true
72
+ message="Command passed"
73
+ else
74
+ message="Command failed"
75
+ fi
76
+ ;;
77
+ output_contains)
78
+ local pattern=$(echo "$item" | yq e '.pattern' -)
79
+ if validate_output_contains "$output" "$pattern"; then
80
+ passed=true
81
+ message="Pattern found"
82
+ else
83
+ message="Pattern not found"
84
+ fi
85
+ ;;
86
+ file_changed)
87
+ local pattern=$(echo "$item" | yq e '.pattern' -)
88
+ if validate_file_changed "$pattern"; then
89
+ passed=true
90
+ message="Files changed"
91
+ else
92
+ message="No files changed"
93
+ fi
94
+ ;;
95
+ hook)
96
+ local script=$(echo "$item" | yq e '.script' -)
97
+ if validate_hook "$script" "$output"; then
98
+ passed=true
99
+ message="Hook passed"
100
+ else
101
+ message="Hook failed"
102
+ fi
103
+ ;;
104
+ human)
105
+ local prompt=$(echo "$item" | yq e '.prompt // ""' -)
106
+ if validate_human "$name" "$prompt"; then
107
+ passed=true
108
+ message="Human approved"
109
+ else
110
+ message="Human rejected"
111
+ fi
112
+ ;;
113
+ *)
114
+ message="Unknown validator type: $type"
115
+ ;;
116
+ esac
117
+
118
+ # Print result
119
+ if [[ "$passed" == "true" ]]; then
120
+ echo -e " ${C_GREEN}✓${C_RESET} [$id] $name - ${C_GREEN}${MSG_DOD_ITEM_PASSED}${C_RESET}"
121
+ else
122
+ if [[ "$required" == "true" ]]; then
123
+ echo -e " ${C_RED}✗${C_RESET} [$id] $name - ${C_RED}${MSG_DOD_ITEM_FAILED}${C_RESET} (${MSG_DOD_REQUIRED})"
124
+ all_required_passed=false
125
+ else
126
+ echo -e " ${C_YELLOW}○${C_RESET} [$id] $name - ${C_YELLOW}${MSG_DOD_ITEM_SKIPPED}${C_RESET} (${MSG_DOD_OPTIONAL})"
127
+ fi
128
+ fi
129
+
130
+ # Store result
131
+ results+=("{\"id\":\"$id\",\"passed\":$passed,\"required\":$required,\"message\":\"$message\"}")
132
+ done
133
+
134
+ # Return overall result
135
+ if [[ "$all_required_passed" == "true" ]]; then
136
+ echo -e "\n ${C_GREEN}${MSG_DOD_ALL_REQUIRED_PASSED}${C_RESET}"
137
+ return 0
138
+ else
139
+ echo -e "\n ${C_RED}${MSG_DOD_MISSING_REQUIRED}${C_RESET}"
140
+ return 1
141
+ fi
142
+ }
143
+
144
+ # =============================================================================
145
+ # Simple Completion Check
146
+ # =============================================================================
147
+
148
+ validate_simple_completion() {
149
+ local output="$1"
150
+
151
+ if echo "$output" | grep -qF "$DOD_COMPLETION_MARKER"; then
152
+ echo -e " ${C_GREEN}✓${C_RESET} Completion marker found"
153
+ return 0
154
+ else
155
+ echo -e " ${C_YELLOW}○${C_RESET} Completion marker not found"
156
+ return 1
157
+ fi
158
+ }
159
+
160
+ # =============================================================================
161
+ # Validator: Command
162
+ # =============================================================================
163
+
164
+ validate_command() {
165
+ local cmd="$1"
166
+
167
+ echo -e " ${C_DIM}${MSG_VALIDATOR_COMMAND}: $cmd${C_RESET}"
168
+
169
+ # Execute the command
170
+ if eval "$cmd" > /dev/null 2>&1; then
171
+ return 0
172
+ else
173
+ return 1
174
+ fi
175
+ }
176
+
177
+ # =============================================================================
178
+ # Validator: Output Contains Pattern
179
+ # =============================================================================
180
+
181
+ validate_output_contains() {
182
+ local output="$1"
183
+ local pattern="$2"
184
+
185
+ echo -e " ${C_DIM}${MSG_VALIDATOR_OUTPUT}: $pattern${C_RESET}"
186
+
187
+ if echo "$output" | grep -qE "$pattern"; then
188
+ return 0
189
+ else
190
+ return 1
191
+ fi
192
+ }
193
+
194
+ # =============================================================================
195
+ # Validator: File Changed
196
+ # =============================================================================
197
+
198
+ validate_file_changed() {
199
+ local pattern="$1"
200
+
201
+ echo -e " ${C_DIM}${MSG_VALIDATOR_FILE}: $pattern${C_RESET}"
202
+
203
+ # Use git to check for changes matching pattern
204
+ if command -v git &> /dev/null && git rev-parse --git-dir &> /dev/null; then
205
+ local changes=$(git status --porcelain 2>/dev/null | grep -E "$pattern" | wc -l)
206
+ if [[ $changes -gt 0 ]]; then
207
+ return 0
208
+ fi
209
+ else
210
+ # Fallback: check for recently modified files
211
+ local changes=$(find . -name "$pattern" -mmin -5 2>/dev/null | wc -l)
212
+ if [[ $changes -gt 0 ]]; then
213
+ return 0
214
+ fi
215
+ fi
216
+
217
+ return 1
218
+ }
219
+
220
+ # =============================================================================
221
+ # Validator: Hook
222
+ # =============================================================================
223
+
224
+ validate_hook() {
225
+ local script="$1"
226
+ local output="$2"
227
+
228
+ echo -e " ${C_DIM}${MSG_VALIDATOR_HOOK}: $script${C_RESET}"
229
+
230
+ # Check if script exists
231
+ if [[ ! -f "$script" ]]; then
232
+ echo -e " ${C_RED}Hook script not found: $script${C_RESET}"
233
+ return 1
234
+ fi
235
+
236
+ # Prepare input JSON (same format as Claude hooks)
237
+ local input_json=$(jq -n \
238
+ --arg cwd "$PWD" \
239
+ --arg output "$output" \
240
+ '{cwd: $cwd, output: $output}')
241
+
242
+ # Execute hook with JSON input
243
+ local result
244
+ result=$(echo "$input_json" | bash "$script" 2>&1)
245
+ local exit_code=$?
246
+
247
+ # Check exit code (0 = continue/pass, 2 = block/fail)
248
+ if [[ $exit_code -eq 0 ]]; then
249
+ return 0
250
+ elif [[ $exit_code -eq 2 ]]; then
251
+ return 1
252
+ else
253
+ # Other exit codes treated as errors
254
+ echo -e " ${C_YELLOW}Hook exited with code $exit_code${C_RESET}"
255
+ return 1
256
+ fi
257
+ }
258
+
259
+ # =============================================================================
260
+ # Validator: Human
261
+ # =============================================================================
262
+
263
+ validate_human() {
264
+ local name="$1"
265
+ local prompt="$2"
266
+
267
+ echo -e "\n ${C_MAGENTA}${MSG_DOD_HUMAN_GATE}: $name${C_RESET}"
268
+
269
+ # Default prompt if not provided
270
+ if [[ -z "$prompt" ]]; then
271
+ prompt="${MSG_DOD_HUMAN_PROMPT}"
272
+ fi
273
+
274
+ # Ask for human input
275
+ echo -ne " ${C_BOLD}$prompt ${C_RESET}"
276
+ read -r response
277
+
278
+ # Check response
279
+ case "${response,,}" in
280
+ y|yes|o|oui|s|si|j|ja|sim)
281
+ return 0
282
+ ;;
283
+ *)
284
+ return 1
285
+ ;;
286
+ esac
287
+ }
288
+
289
+ # =============================================================================
290
+ # DoD Result Storage
291
+ # =============================================================================
292
+
293
+ store_dod_results() {
294
+ local session_id="$1"
295
+ local results="$2"
296
+
297
+ local session_dir="$RALPH_SESSION_BASE/sessions/$session_id"
298
+ local state_file="$session_dir/state.json"
299
+
300
+ if [[ -f "$state_file" ]]; then
301
+ local tmp_file=$(mktemp)
302
+ echo "$results" | jq -s '.' > "$session_dir/dod_results.json"
303
+ jq --slurpfile results "$session_dir/dod_results.json" '.dod_results = $results[0]' "$state_file" > "$tmp_file"
304
+ mv "$tmp_file" "$state_file"
305
+ fi
306
+ }
@@ -0,0 +1,232 @@
1
+ #!/bin/bash
2
+ # =============================================================================
3
+ # Ralph Wiggum - Main Loop Module
4
+ # Core iteration loop logic and Claude invocation
5
+ # =============================================================================
6
+
7
+ # Claude command configuration
8
+ CLAUDE_COMMAND="${CLAUDE_COMMAND:-claude}"
9
+ CLAUDE_ARGS="${CLAUDE_ARGS:---dangerously-skip-permissions}"
10
+
11
+ # =============================================================================
12
+ # Claude Invocation
13
+ # =============================================================================
14
+
15
+ invoke_claude() {
16
+ local session_id="$1"
17
+ local prompt="$2"
18
+ local timeout="$3"
19
+
20
+ # Build the command
21
+ local cmd="$CLAUDE_COMMAND"
22
+
23
+ # Add continue flag if we have a session
24
+ if [[ -n "$session_id" ]]; then
25
+ cmd="$cmd --continue"
26
+ fi
27
+
28
+ # Add prompt
29
+ cmd="$cmd -p"
30
+
31
+ # Log the invocation
32
+ log_session "$session_id" "INFO" "Invoking Claude: $cmd \"${prompt:0:100}...\""
33
+
34
+ # Execute with timeout
35
+ local timeout_sec=$((timeout / 1000))
36
+ local output
37
+ local exit_code
38
+
39
+ # Use timeout command if available
40
+ if command -v timeout &> /dev/null; then
41
+ output=$(timeout "${timeout_sec}s" $cmd "$prompt" 2>&1)
42
+ exit_code=$?
43
+
44
+ # Check for timeout (exit code 124)
45
+ if [[ $exit_code -eq 124 ]]; then
46
+ log_session "$session_id" "ERROR" "Claude invocation timed out after ${timeout_sec}s"
47
+ return 124
48
+ fi
49
+ else
50
+ # Fallback without timeout
51
+ output=$($cmd "$prompt" 2>&1)
52
+ exit_code=$?
53
+ fi
54
+
55
+ # Log result
56
+ if [[ $exit_code -eq 0 ]]; then
57
+ log_session "$session_id" "INFO" "Claude responded (${#output} chars)"
58
+ else
59
+ log_session "$session_id" "ERROR" "Claude failed with exit code $exit_code"
60
+ fi
61
+
62
+ # Output the response
63
+ echo "$output"
64
+ return $exit_code
65
+ }
66
+
67
+ # =============================================================================
68
+ # Output Processing
69
+ # =============================================================================
70
+
71
+ process_output() {
72
+ local output="$1"
73
+ local session_id="$2"
74
+
75
+ # Store the raw output
76
+ local session_dir="$RALPH_SESSION_BASE/sessions/$session_id"
77
+ local output_file="$session_dir/last_output.txt"
78
+
79
+ echo "$output" > "$output_file"
80
+
81
+ # Extract any structured data if present
82
+ # Look for JSON blocks, completion markers, etc.
83
+
84
+ # Check for completion marker
85
+ if echo "$output" | grep -q "$DEFAULT_COMPLETION_MARKER"; then
86
+ log_session "$session_id" "INFO" "Completion marker found in output"
87
+ return 0
88
+ fi
89
+
90
+ return 1
91
+ }
92
+
93
+ # =============================================================================
94
+ # File Change Detection
95
+ # =============================================================================
96
+
97
+ detect_file_changes() {
98
+ local session_id="$1"
99
+
100
+ # Use git to detect changes if available
101
+ if command -v git &> /dev/null && git rev-parse --git-dir &> /dev/null; then
102
+ local changes=$(git status --porcelain 2>/dev/null | wc -l)
103
+ log_session "$session_id" "DEBUG" "Detected $changes file changes"
104
+ echo "$changes"
105
+ else
106
+ # Fallback: check modification times in common source directories
107
+ local changes=0
108
+ for dir in src lib app tests; do
109
+ if [[ -d "$dir" ]]; then
110
+ local recent=$(find "$dir" -type f -mmin -1 2>/dev/null | wc -l)
111
+ changes=$((changes + recent))
112
+ fi
113
+ done
114
+ echo "$changes"
115
+ fi
116
+ }
117
+
118
+ # =============================================================================
119
+ # Progress Detection
120
+ # =============================================================================
121
+
122
+ check_progress() {
123
+ local session_id="$1"
124
+ local output="$2"
125
+ local previous_length="$3"
126
+
127
+ local current_length=${#output}
128
+ local file_changes=$(detect_file_changes "$session_id")
129
+
130
+ # Progress indicators
131
+ local has_progress=false
132
+
133
+ # 1. Files were changed
134
+ if [[ $file_changes -gt 0 ]]; then
135
+ has_progress=true
136
+ log_session "$session_id" "INFO" "Progress: $file_changes files changed"
137
+ fi
138
+
139
+ # 2. Output length is significant (not just errors)
140
+ if [[ $current_length -gt 100 ]]; then
141
+ # Check if output contains actual work indicators
142
+ if echo "$output" | grep -qE "(created|modified|fixed|implemented|added|updated)" ; then
143
+ has_progress=true
144
+ log_session "$session_id" "INFO" "Progress: Work indicators found in output"
145
+ fi
146
+ fi
147
+
148
+ # Update metrics
149
+ update_session_state "$session_id" "metrics.file_changes" "$file_changes"
150
+
151
+ if [[ "$has_progress" == "true" ]]; then
152
+ return 0
153
+ else
154
+ return 1
155
+ fi
156
+ }
157
+
158
+ # =============================================================================
159
+ # Error Detection
160
+ # =============================================================================
161
+
162
+ detect_errors() {
163
+ local output="$1"
164
+ local errors=()
165
+
166
+ # Common error patterns
167
+ local error_patterns=(
168
+ "Error:"
169
+ "error:"
170
+ "FAILED"
171
+ "Exception"
172
+ "fatal:"
173
+ "Cannot"
174
+ "could not"
175
+ "Permission denied"
176
+ "No such file"
177
+ "command not found"
178
+ )
179
+
180
+ for pattern in "${error_patterns[@]}"; do
181
+ if echo "$output" | grep -q "$pattern"; then
182
+ errors+=("$pattern")
183
+ fi
184
+ done
185
+
186
+ if [[ ${#errors[@]} -gt 0 ]]; then
187
+ printf '%s\n' "${errors[@]}"
188
+ return 1
189
+ fi
190
+
191
+ return 0
192
+ }
193
+
194
+ # =============================================================================
195
+ # Loop State
196
+ # =============================================================================
197
+
198
+ get_loop_state() {
199
+ local session_id="$1"
200
+ local state=$(get_session_state "$session_id")
201
+
202
+ echo "$state" | jq '{
203
+ iteration: .current_iteration,
204
+ status: .status,
205
+ circuit_breaker: .circuit_breaker,
206
+ dod_results: .dod_results
207
+ }'
208
+ }
209
+
210
+ should_continue_loop() {
211
+ local session_id="$1"
212
+ local iteration="$2"
213
+ local max_iterations="$3"
214
+
215
+ # Check max iterations
216
+ if [[ $iteration -ge $max_iterations ]]; then
217
+ return 1
218
+ fi
219
+
220
+ # Check circuit breaker
221
+ if check_circuit_breaker; then
222
+ return 1
223
+ fi
224
+
225
+ # Check session status
226
+ local status=$(get_session_state "$session_id" | jq -r '.status')
227
+ if [[ "$status" == "completed" || "$status" == "failed" ]]; then
228
+ return 1
229
+ fi
230
+
231
+ return 0
232
+ }