bmalph 2.7.5 → 2.7.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -5
- package/dist/commands/doctor-runtime-checks.js +104 -86
- package/dist/commands/doctor-runtime-checks.js.map +1 -1
- package/dist/installer/bmad-assets.js +182 -0
- package/dist/installer/bmad-assets.js.map +1 -0
- package/dist/installer/commands.js +324 -0
- package/dist/installer/commands.js.map +1 -0
- package/dist/installer/install.js +42 -0
- package/dist/installer/install.js.map +1 -0
- package/dist/installer/metadata.js +56 -0
- package/dist/installer/metadata.js.map +1 -0
- package/dist/installer/project-files.js +169 -0
- package/dist/installer/project-files.js.map +1 -0
- package/dist/installer/ralph-assets.js +91 -0
- package/dist/installer/ralph-assets.js.map +1 -0
- package/dist/installer/template-files.js +168 -0
- package/dist/installer/template-files.js.map +1 -0
- package/dist/installer/types.js +2 -0
- package/dist/installer/types.js.map +1 -0
- package/dist/installer.js +5 -843
- package/dist/installer.js.map +1 -1
- package/dist/transition/artifact-loading.js +91 -0
- package/dist/transition/artifact-loading.js.map +1 -0
- package/dist/transition/context-output.js +85 -0
- package/dist/transition/context-output.js.map +1 -0
- package/dist/transition/fix-plan-sync.js +119 -0
- package/dist/transition/fix-plan-sync.js.map +1 -0
- package/dist/transition/orchestration.js +25 -362
- package/dist/transition/orchestration.js.map +1 -1
- package/dist/transition/specs-sync.js +78 -2
- package/dist/transition/specs-sync.js.map +1 -1
- package/dist/utils/ralph-runtime-state.js +222 -0
- package/dist/utils/ralph-runtime-state.js.map +1 -0
- package/dist/utils/state.js +17 -16
- package/dist/utils/state.js.map +1 -1
- package/dist/utils/validate.js +16 -0
- package/dist/utils/validate.js.map +1 -1
- package/dist/watch/renderer.js +48 -6
- package/dist/watch/renderer.js.map +1 -1
- package/dist/watch/state-reader.js +79 -44
- package/dist/watch/state-reader.js.map +1 -1
- package/package.json +1 -1
- package/ralph/RALPH-REFERENCE.md +33 -13
- package/ralph/drivers/claude-code.sh +25 -0
- package/ralph/drivers/codex.sh +11 -0
- package/ralph/drivers/copilot.sh +11 -0
- package/ralph/drivers/cursor.sh +11 -0
- package/ralph/lib/circuit_breaker.sh +3 -3
- package/ralph/lib/date_utils.sh +28 -9
- package/ralph/lib/response_analyzer.sh +127 -7
- package/ralph/ralph_loop.sh +464 -121
- package/ralph/templates/PROMPT.md +5 -0
- package/ralph/templates/ralphrc.template +14 -4
package/ralph/ralph_loop.sh
CHANGED
|
@@ -40,9 +40,32 @@ _env_MAX_CALLS_PER_HOUR="${MAX_CALLS_PER_HOUR:-}"
|
|
|
40
40
|
_env_CLAUDE_TIMEOUT_MINUTES="${CLAUDE_TIMEOUT_MINUTES:-}"
|
|
41
41
|
_env_CLAUDE_OUTPUT_FORMAT="${CLAUDE_OUTPUT_FORMAT:-}"
|
|
42
42
|
_env_CLAUDE_ALLOWED_TOOLS="${CLAUDE_ALLOWED_TOOLS:-}"
|
|
43
|
+
_env_has_CLAUDE_PERMISSION_MODE="${CLAUDE_PERMISSION_MODE+x}"
|
|
44
|
+
_env_CLAUDE_PERMISSION_MODE="${CLAUDE_PERMISSION_MODE:-}"
|
|
43
45
|
_env_CLAUDE_USE_CONTINUE="${CLAUDE_USE_CONTINUE:-}"
|
|
44
46
|
_env_CLAUDE_SESSION_EXPIRY_HOURS="${CLAUDE_SESSION_EXPIRY_HOURS:-}"
|
|
47
|
+
_env_ALLOWED_TOOLS="${ALLOWED_TOOLS:-}"
|
|
48
|
+
_env_SESSION_CONTINUITY="${SESSION_CONTINUITY:-}"
|
|
49
|
+
_env_SESSION_EXPIRY_HOURS="${SESSION_EXPIRY_HOURS:-}"
|
|
50
|
+
_env_PERMISSION_DENIAL_MODE="${PERMISSION_DENIAL_MODE:-}"
|
|
51
|
+
_env_RALPH_VERBOSE="${RALPH_VERBOSE:-}"
|
|
45
52
|
_env_VERBOSE_PROGRESS="${VERBOSE_PROGRESS:-}"
|
|
53
|
+
|
|
54
|
+
# CLI flags are parsed before main() runs, so capture explicit values separately.
|
|
55
|
+
_CLI_MAX_CALLS_PER_HOUR="${_CLI_MAX_CALLS_PER_HOUR:-}"
|
|
56
|
+
_CLI_CLAUDE_TIMEOUT_MINUTES="${_CLI_CLAUDE_TIMEOUT_MINUTES:-}"
|
|
57
|
+
_CLI_CLAUDE_OUTPUT_FORMAT="${_CLI_CLAUDE_OUTPUT_FORMAT:-}"
|
|
58
|
+
_CLI_ALLOWED_TOOLS="${_CLI_ALLOWED_TOOLS:-}"
|
|
59
|
+
_CLI_SESSION_CONTINUITY="${_CLI_SESSION_CONTINUITY:-}"
|
|
60
|
+
_CLI_SESSION_EXPIRY_HOURS="${_CLI_SESSION_EXPIRY_HOURS:-}"
|
|
61
|
+
_CLI_VERBOSE_PROGRESS="${_CLI_VERBOSE_PROGRESS:-}"
|
|
62
|
+
_cli_MAX_CALLS_PER_HOUR="${MAX_CALLS_PER_HOUR:-}"
|
|
63
|
+
_cli_CLAUDE_TIMEOUT_MINUTES="${CLAUDE_TIMEOUT_MINUTES:-}"
|
|
64
|
+
_cli_CLAUDE_OUTPUT_FORMAT="${CLAUDE_OUTPUT_FORMAT:-}"
|
|
65
|
+
_cli_CLAUDE_ALLOWED_TOOLS="${CLAUDE_ALLOWED_TOOLS:-}"
|
|
66
|
+
_cli_CLAUDE_USE_CONTINUE="${CLAUDE_USE_CONTINUE:-}"
|
|
67
|
+
_cli_CLAUDE_SESSION_EXPIRY_HOURS="${CLAUDE_SESSION_EXPIRY_HOURS:-}"
|
|
68
|
+
_cli_VERBOSE_PROGRESS="${VERBOSE_PROGRESS:-}"
|
|
46
69
|
_env_CB_COOLDOWN_MINUTES="${CB_COOLDOWN_MINUTES:-}"
|
|
47
70
|
_env_CB_AUTO_RESET="${CB_AUTO_RESET:-}"
|
|
48
71
|
|
|
@@ -50,11 +73,15 @@ _env_CB_AUTO_RESET="${CB_AUTO_RESET:-}"
|
|
|
50
73
|
MAX_CALLS_PER_HOUR="${MAX_CALLS_PER_HOUR:-100}"
|
|
51
74
|
VERBOSE_PROGRESS="${VERBOSE_PROGRESS:-false}"
|
|
52
75
|
CLAUDE_TIMEOUT_MINUTES="${CLAUDE_TIMEOUT_MINUTES:-15}"
|
|
76
|
+
DEFAULT_CLAUDE_ALLOWED_TOOLS="Write,Read,Edit,MultiEdit,Glob,Grep,Task,TodoWrite,WebFetch,WebSearch,NotebookEdit,Bash"
|
|
77
|
+
DEFAULT_PERMISSION_DENIAL_MODE="continue"
|
|
53
78
|
|
|
54
79
|
# Modern Claude CLI configuration (Phase 1.1)
|
|
55
80
|
CLAUDE_OUTPUT_FORMAT="${CLAUDE_OUTPUT_FORMAT:-json}"
|
|
56
|
-
CLAUDE_ALLOWED_TOOLS="${CLAUDE_ALLOWED_TOOLS
|
|
81
|
+
CLAUDE_ALLOWED_TOOLS="${CLAUDE_ALLOWED_TOOLS:-$DEFAULT_CLAUDE_ALLOWED_TOOLS}"
|
|
82
|
+
CLAUDE_PERMISSION_MODE="${CLAUDE_PERMISSION_MODE:-auto}"
|
|
57
83
|
CLAUDE_USE_CONTINUE="${CLAUDE_USE_CONTINUE:-true}"
|
|
84
|
+
PERMISSION_DENIAL_MODE="${PERMISSION_DENIAL_MODE:-$DEFAULT_PERMISSION_DENIAL_MODE}"
|
|
58
85
|
CLAUDE_SESSION_FILE="$RALPH_DIR/.claude_session_id" # Session ID persistence file
|
|
59
86
|
CLAUDE_MIN_VERSION="2.0.76" # Minimum required Claude CLI version
|
|
60
87
|
|
|
@@ -80,6 +107,7 @@ VALID_TOOL_PATTERNS=(
|
|
|
80
107
|
"TodoWrite"
|
|
81
108
|
"WebFetch"
|
|
82
109
|
"WebSearch"
|
|
110
|
+
"AskUserQuestion"
|
|
83
111
|
"Bash"
|
|
84
112
|
"Bash(git *)"
|
|
85
113
|
"Bash(npm *)"
|
|
@@ -88,6 +116,8 @@ VALID_TOOL_PATTERNS=(
|
|
|
88
116
|
"Bash(node *)"
|
|
89
117
|
"NotebookEdit"
|
|
90
118
|
)
|
|
119
|
+
ALLOWED_TOOLS_IGNORED_WARNED=false
|
|
120
|
+
PERMISSION_DENIAL_ACTION=""
|
|
91
121
|
|
|
92
122
|
# Exit detection configuration
|
|
93
123
|
EXIT_SIGNALS_FILE="$RALPH_DIR/.exit_signals"
|
|
@@ -131,7 +161,9 @@ resolve_ralphrc_file() {
|
|
|
131
161
|
# - MAX_CALLS_PER_HOUR
|
|
132
162
|
# - CLAUDE_TIMEOUT_MINUTES
|
|
133
163
|
# - CLAUDE_OUTPUT_FORMAT
|
|
134
|
-
# -
|
|
164
|
+
# - CLAUDE_PERMISSION_MODE
|
|
165
|
+
# - ALLOWED_TOOLS (mapped to CLAUDE_ALLOWED_TOOLS for Claude Code only)
|
|
166
|
+
# - PERMISSION_DENIAL_MODE
|
|
135
167
|
# - SESSION_CONTINUITY (mapped to CLAUDE_USE_CONTINUE)
|
|
136
168
|
# - SESSION_EXPIRY_HOURS (mapped to CLAUDE_SESSION_EXPIRY_HOURS)
|
|
137
169
|
# - CB_NO_PROGRESS_THRESHOLD
|
|
@@ -155,6 +187,9 @@ load_ralphrc() {
|
|
|
155
187
|
if [[ -n "${ALLOWED_TOOLS:-}" ]]; then
|
|
156
188
|
CLAUDE_ALLOWED_TOOLS="$ALLOWED_TOOLS"
|
|
157
189
|
fi
|
|
190
|
+
if [[ -n "${PERMISSION_DENIAL_MODE:-}" ]]; then
|
|
191
|
+
PERMISSION_DENIAL_MODE="$PERMISSION_DENIAL_MODE"
|
|
192
|
+
fi
|
|
158
193
|
if [[ -n "${SESSION_CONTINUITY:-}" ]]; then
|
|
159
194
|
CLAUDE_USE_CONTINUE="$SESSION_CONTINUITY"
|
|
160
195
|
fi
|
|
@@ -167,22 +202,57 @@ load_ralphrc() {
|
|
|
167
202
|
|
|
168
203
|
# Restore ONLY values that were explicitly set via environment variables
|
|
169
204
|
# (not script defaults). The _env_* variables were captured BEFORE defaults were set.
|
|
170
|
-
#
|
|
205
|
+
# Internal CLAUDE_* variables are kept for backward compatibility.
|
|
171
206
|
[[ -n "$_env_MAX_CALLS_PER_HOUR" ]] && MAX_CALLS_PER_HOUR="$_env_MAX_CALLS_PER_HOUR"
|
|
172
207
|
[[ -n "$_env_CLAUDE_TIMEOUT_MINUTES" ]] && CLAUDE_TIMEOUT_MINUTES="$_env_CLAUDE_TIMEOUT_MINUTES"
|
|
173
208
|
[[ -n "$_env_CLAUDE_OUTPUT_FORMAT" ]] && CLAUDE_OUTPUT_FORMAT="$_env_CLAUDE_OUTPUT_FORMAT"
|
|
174
209
|
[[ -n "$_env_CLAUDE_ALLOWED_TOOLS" ]] && CLAUDE_ALLOWED_TOOLS="$_env_CLAUDE_ALLOWED_TOOLS"
|
|
210
|
+
if [[ "$_env_has_CLAUDE_PERMISSION_MODE" == "x" ]]; then
|
|
211
|
+
CLAUDE_PERMISSION_MODE="$_env_CLAUDE_PERMISSION_MODE"
|
|
212
|
+
fi
|
|
175
213
|
[[ -n "$_env_CLAUDE_USE_CONTINUE" ]] && CLAUDE_USE_CONTINUE="$_env_CLAUDE_USE_CONTINUE"
|
|
176
214
|
[[ -n "$_env_CLAUDE_SESSION_EXPIRY_HOURS" ]] && CLAUDE_SESSION_EXPIRY_HOURS="$_env_CLAUDE_SESSION_EXPIRY_HOURS"
|
|
215
|
+
[[ -n "$_env_PERMISSION_DENIAL_MODE" ]] && PERMISSION_DENIAL_MODE="$_env_PERMISSION_DENIAL_MODE"
|
|
177
216
|
[[ -n "$_env_VERBOSE_PROGRESS" ]] && VERBOSE_PROGRESS="$_env_VERBOSE_PROGRESS"
|
|
217
|
+
|
|
218
|
+
# Public aliases are the preferred external interface and win over the
|
|
219
|
+
# legacy internal environment variables when both are explicitly set.
|
|
220
|
+
[[ -n "$_env_ALLOWED_TOOLS" ]] && CLAUDE_ALLOWED_TOOLS="$_env_ALLOWED_TOOLS"
|
|
221
|
+
[[ -n "$_env_SESSION_CONTINUITY" ]] && CLAUDE_USE_CONTINUE="$_env_SESSION_CONTINUITY"
|
|
222
|
+
[[ -n "$_env_SESSION_EXPIRY_HOURS" ]] && CLAUDE_SESSION_EXPIRY_HOURS="$_env_SESSION_EXPIRY_HOURS"
|
|
223
|
+
[[ -n "$_env_RALPH_VERBOSE" ]] && VERBOSE_PROGRESS="$_env_RALPH_VERBOSE"
|
|
224
|
+
|
|
225
|
+
# CLI flags are the highest-priority runtime inputs because they are
|
|
226
|
+
# parsed before main() and would otherwise be overwritten by .ralphrc.
|
|
227
|
+
# Keep every config-backed CLI flag here so the precedence contract stays
|
|
228
|
+
# consistent: CLI > public env aliases > internal env vars > config.
|
|
229
|
+
[[ "$_CLI_MAX_CALLS_PER_HOUR" == "true" ]] && MAX_CALLS_PER_HOUR="$_cli_MAX_CALLS_PER_HOUR"
|
|
230
|
+
[[ "$_CLI_CLAUDE_TIMEOUT_MINUTES" == "true" ]] && CLAUDE_TIMEOUT_MINUTES="$_cli_CLAUDE_TIMEOUT_MINUTES"
|
|
231
|
+
[[ "$_CLI_CLAUDE_OUTPUT_FORMAT" == "true" ]] && CLAUDE_OUTPUT_FORMAT="$_cli_CLAUDE_OUTPUT_FORMAT"
|
|
232
|
+
[[ "$_CLI_ALLOWED_TOOLS" == "true" ]] && CLAUDE_ALLOWED_TOOLS="$_cli_CLAUDE_ALLOWED_TOOLS"
|
|
233
|
+
[[ "$_CLI_SESSION_CONTINUITY" == "true" ]] && CLAUDE_USE_CONTINUE="$_cli_CLAUDE_USE_CONTINUE"
|
|
234
|
+
[[ "$_CLI_SESSION_EXPIRY_HOURS" == "true" ]] && CLAUDE_SESSION_EXPIRY_HOURS="$_cli_CLAUDE_SESSION_EXPIRY_HOURS"
|
|
235
|
+
[[ "$_CLI_VERBOSE_PROGRESS" == "true" ]] && VERBOSE_PROGRESS="$_cli_VERBOSE_PROGRESS"
|
|
178
236
|
[[ -n "$_env_CB_COOLDOWN_MINUTES" ]] && CB_COOLDOWN_MINUTES="$_env_CB_COOLDOWN_MINUTES"
|
|
179
237
|
[[ -n "$_env_CB_AUTO_RESET" ]] && CB_AUTO_RESET="$_env_CB_AUTO_RESET"
|
|
180
238
|
|
|
239
|
+
normalize_claude_permission_mode
|
|
181
240
|
RALPHRC_FILE="$config_file"
|
|
182
241
|
RALPHRC_LOADED=true
|
|
183
242
|
return 0
|
|
184
243
|
}
|
|
185
244
|
|
|
245
|
+
driver_supports_tool_allowlist() {
|
|
246
|
+
return 1
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
driver_permission_denial_help() {
|
|
250
|
+
echo " - Review the active driver's permission or approval settings."
|
|
251
|
+
echo " - ALLOWED_TOOLS in $RALPHRC_FILE only applies to the Claude Code driver."
|
|
252
|
+
echo " - Keep CLAUDE_PERMISSION_MODE=auto for unattended Claude Code loops."
|
|
253
|
+
echo " - After updating permissions, reset the session and restart the loop."
|
|
254
|
+
}
|
|
255
|
+
|
|
186
256
|
# Source platform driver
|
|
187
257
|
load_platform_driver() {
|
|
188
258
|
local driver_file="$SCRIPT_DIR/drivers/${PLATFORM_DRIVER}.sh"
|
|
@@ -323,8 +393,8 @@ setup_tmux_session() {
|
|
|
323
393
|
if [[ "$CLAUDE_TIMEOUT_MINUTES" != "15" ]]; then
|
|
324
394
|
ralph_cmd="$ralph_cmd --timeout $CLAUDE_TIMEOUT_MINUTES"
|
|
325
395
|
fi
|
|
326
|
-
# Forward --allowed-tools
|
|
327
|
-
if [[ "$CLAUDE_ALLOWED_TOOLS" != "
|
|
396
|
+
# Forward --allowed-tools only for drivers that support tool allowlists
|
|
397
|
+
if driver_supports_tool_allowlist && [[ "$CLAUDE_ALLOWED_TOOLS" != "$DEFAULT_CLAUDE_ALLOWED_TOOLS" ]]; then
|
|
328
398
|
ralph_cmd="$ralph_cmd --allowed-tools '$CLAUDE_ALLOWED_TOOLS'"
|
|
329
399
|
fi
|
|
330
400
|
# Forward --no-continue if session continuity disabled
|
|
@@ -443,6 +513,175 @@ update_status() {
|
|
|
443
513
|
}' > "$STATUS_FILE"
|
|
444
514
|
}
|
|
445
515
|
|
|
516
|
+
validate_permission_denial_mode() {
|
|
517
|
+
local mode=$1
|
|
518
|
+
|
|
519
|
+
case "$mode" in
|
|
520
|
+
continue|halt|threshold)
|
|
521
|
+
return 0
|
|
522
|
+
;;
|
|
523
|
+
*)
|
|
524
|
+
echo "Error: Invalid PERMISSION_DENIAL_MODE: '$mode'"
|
|
525
|
+
echo "Valid modes: continue halt threshold"
|
|
526
|
+
return 1
|
|
527
|
+
;;
|
|
528
|
+
esac
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
normalize_claude_permission_mode() {
|
|
532
|
+
if [[ -z "${CLAUDE_PERMISSION_MODE:-}" ]]; then
|
|
533
|
+
CLAUDE_PERMISSION_MODE="auto"
|
|
534
|
+
fi
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
validate_claude_permission_mode() {
|
|
538
|
+
local mode=$1
|
|
539
|
+
|
|
540
|
+
case "$mode" in
|
|
541
|
+
auto|acceptEdits|bypassPermissions|default|dontAsk|plan)
|
|
542
|
+
return 0
|
|
543
|
+
;;
|
|
544
|
+
*)
|
|
545
|
+
echo "Error: Invalid CLAUDE_PERMISSION_MODE: '$mode'"
|
|
546
|
+
echo "Valid modes: auto acceptEdits bypassPermissions default dontAsk plan"
|
|
547
|
+
return 1
|
|
548
|
+
;;
|
|
549
|
+
esac
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
warn_if_allowed_tools_ignored() {
|
|
553
|
+
if driver_supports_tool_allowlist; then
|
|
554
|
+
return 0
|
|
555
|
+
fi
|
|
556
|
+
|
|
557
|
+
if [[ "$ALLOWED_TOOLS_IGNORED_WARNED" == "true" ]]; then
|
|
558
|
+
return 0
|
|
559
|
+
fi
|
|
560
|
+
|
|
561
|
+
if [[ "${_CLI_ALLOWED_TOOLS:-}" == "true" || "$CLAUDE_ALLOWED_TOOLS" != "$DEFAULT_CLAUDE_ALLOWED_TOOLS" ]]; then
|
|
562
|
+
log_status "WARN" "ALLOWED_TOOLS/--allowed-tools is ignored by $DRIVER_DISPLAY_NAME."
|
|
563
|
+
ALLOWED_TOOLS_IGNORED_WARNED=true
|
|
564
|
+
fi
|
|
565
|
+
|
|
566
|
+
return 0
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
show_current_allowed_tools() {
|
|
570
|
+
if ! driver_supports_tool_allowlist; then
|
|
571
|
+
return 0
|
|
572
|
+
fi
|
|
573
|
+
|
|
574
|
+
if [[ -f "$RALPHRC_FILE" ]]; then
|
|
575
|
+
local current_tools=$(grep "^ALLOWED_TOOLS=" "$RALPHRC_FILE" 2>/dev/null | cut -d= -f2- | tr -d '"')
|
|
576
|
+
if [[ -n "$current_tools" ]]; then
|
|
577
|
+
echo -e "${BLUE}Current ALLOWED_TOOLS:${NC} $current_tools"
|
|
578
|
+
echo ""
|
|
579
|
+
fi
|
|
580
|
+
fi
|
|
581
|
+
|
|
582
|
+
return 0
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
response_analysis_has_permission_denials() {
|
|
586
|
+
if [[ ! -f "$RESPONSE_ANALYSIS_FILE" ]]; then
|
|
587
|
+
return 1
|
|
588
|
+
fi
|
|
589
|
+
|
|
590
|
+
local has_permission_denials
|
|
591
|
+
has_permission_denials=$(jq -r '.analysis.has_permission_denials // false' "$RESPONSE_ANALYSIS_FILE" 2>/dev/null || echo "false")
|
|
592
|
+
|
|
593
|
+
[[ "$has_permission_denials" == "true" ]]
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
get_response_analysis_denied_commands() {
|
|
597
|
+
if [[ ! -f "$RESPONSE_ANALYSIS_FILE" ]]; then
|
|
598
|
+
echo "unknown"
|
|
599
|
+
return 0
|
|
600
|
+
fi
|
|
601
|
+
|
|
602
|
+
jq -r '.analysis.denied_commands | join(", ")' "$RESPONSE_ANALYSIS_FILE" 2>/dev/null || echo "unknown"
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
clear_response_analysis_permission_denials() {
|
|
606
|
+
if [[ ! -f "$RESPONSE_ANALYSIS_FILE" ]]; then
|
|
607
|
+
return 0
|
|
608
|
+
fi
|
|
609
|
+
|
|
610
|
+
local tmp_file="$RESPONSE_ANALYSIS_FILE.tmp"
|
|
611
|
+
if jq '
|
|
612
|
+
(.analysis //= {}) |
|
|
613
|
+
.analysis.has_completion_signal = false |
|
|
614
|
+
.analysis.exit_signal = false |
|
|
615
|
+
.analysis.has_permission_denials = false |
|
|
616
|
+
.analysis.permission_denial_count = 0 |
|
|
617
|
+
.analysis.denied_commands = []
|
|
618
|
+
' "$RESPONSE_ANALYSIS_FILE" > "$tmp_file" 2>/dev/null; then
|
|
619
|
+
mv "$tmp_file" "$RESPONSE_ANALYSIS_FILE"
|
|
620
|
+
return 0
|
|
621
|
+
fi
|
|
622
|
+
|
|
623
|
+
rm -f "$tmp_file" 2>/dev/null
|
|
624
|
+
return 1
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
handle_permission_denial() {
|
|
628
|
+
local loop_count=$1
|
|
629
|
+
local denied_cmds=${2:-unknown}
|
|
630
|
+
local calls_made
|
|
631
|
+
calls_made=$(cat "$CALL_COUNT_FILE" 2>/dev/null || echo "0")
|
|
632
|
+
PERMISSION_DENIAL_ACTION=""
|
|
633
|
+
|
|
634
|
+
case "$PERMISSION_DENIAL_MODE" in
|
|
635
|
+
continue|threshold)
|
|
636
|
+
log_status "WARN" "🚫 Permission denied in loop #$loop_count: $denied_cmds"
|
|
637
|
+
log_status "WARN" "PERMISSION_DENIAL_MODE=$PERMISSION_DENIAL_MODE - continuing execution"
|
|
638
|
+
update_status "$loop_count" "$calls_made" "permission_denied" "running"
|
|
639
|
+
PERMISSION_DENIAL_ACTION="continue"
|
|
640
|
+
return 0
|
|
641
|
+
;;
|
|
642
|
+
halt)
|
|
643
|
+
log_status "ERROR" "🚫 Permission denied - halting loop"
|
|
644
|
+
reset_session "permission_denied"
|
|
645
|
+
update_status "$loop_count" "$calls_made" "permission_denied" "halted" "permission_denied"
|
|
646
|
+
|
|
647
|
+
echo ""
|
|
648
|
+
echo -e "${RED}╔════════════════════════════════════════════════════════════╗${NC}"
|
|
649
|
+
echo -e "${RED}║ PERMISSION DENIED - Loop Halted ║${NC}"
|
|
650
|
+
echo -e "${RED}╚════════════════════════════════════════════════════════════╝${NC}"
|
|
651
|
+
echo ""
|
|
652
|
+
echo -e "${YELLOW}$DRIVER_DISPLAY_NAME was denied permission to execute commands.${NC}"
|
|
653
|
+
echo ""
|
|
654
|
+
echo -e "${YELLOW}To fix this:${NC}"
|
|
655
|
+
driver_permission_denial_help
|
|
656
|
+
echo ""
|
|
657
|
+
show_current_allowed_tools
|
|
658
|
+
PERMISSION_DENIAL_ACTION="halt"
|
|
659
|
+
return 0
|
|
660
|
+
;;
|
|
661
|
+
esac
|
|
662
|
+
|
|
663
|
+
return 1
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
consume_current_loop_permission_denial() {
|
|
667
|
+
local loop_count=$1
|
|
668
|
+
PERMISSION_DENIAL_ACTION=""
|
|
669
|
+
|
|
670
|
+
if ! response_analysis_has_permission_denials; then
|
|
671
|
+
return 1
|
|
672
|
+
fi
|
|
673
|
+
|
|
674
|
+
local denied_cmds
|
|
675
|
+
denied_cmds=$(get_response_analysis_denied_commands)
|
|
676
|
+
|
|
677
|
+
if ! clear_response_analysis_permission_denials; then
|
|
678
|
+
log_status "WARN" "Failed to clear permission denial markers from response analysis"
|
|
679
|
+
fi
|
|
680
|
+
|
|
681
|
+
handle_permission_denial "$loop_count" "$denied_cmds"
|
|
682
|
+
return 0
|
|
683
|
+
}
|
|
684
|
+
|
|
446
685
|
# Check if we can make another call
|
|
447
686
|
can_make_call() {
|
|
448
687
|
local calls_made=0
|
|
@@ -508,21 +747,6 @@ should_exit_gracefully() {
|
|
|
508
747
|
|
|
509
748
|
# Check for exit conditions
|
|
510
749
|
|
|
511
|
-
# 0. Permission denials (highest priority - Issue #101)
|
|
512
|
-
# When Claude Code is denied permission to run commands, halt immediately
|
|
513
|
-
# to allow user to update .ralphrc ALLOWED_TOOLS configuration
|
|
514
|
-
if [[ -f "$RESPONSE_ANALYSIS_FILE" ]]; then
|
|
515
|
-
local has_permission_denials=$(jq -r '.analysis.has_permission_denials // false' "$RESPONSE_ANALYSIS_FILE" 2>/dev/null || echo "false")
|
|
516
|
-
if [[ "$has_permission_denials" == "true" ]]; then
|
|
517
|
-
local denied_count=$(jq -r '.analysis.permission_denial_count // 0' "$RESPONSE_ANALYSIS_FILE" 2>/dev/null || echo "0")
|
|
518
|
-
local denied_cmds=$(jq -r '.analysis.denied_commands | join(", ")' "$RESPONSE_ANALYSIS_FILE" 2>/dev/null || echo "unknown")
|
|
519
|
-
log_status "WARN" "🚫 Permission denied for $denied_count command(s): $denied_cmds"
|
|
520
|
-
log_status "WARN" "Update ALLOWED_TOOLS in .ralphrc to include the required tools"
|
|
521
|
-
echo "permission_denied"
|
|
522
|
-
return 0
|
|
523
|
-
fi
|
|
524
|
-
fi
|
|
525
|
-
|
|
526
750
|
# 1. Too many consecutive test-only loops
|
|
527
751
|
if [[ $recent_test_loops -ge $MAX_CONSECUTIVE_TEST_LOOPS ]]; then
|
|
528
752
|
log_status "WARN" "Exit condition: Too many test-focused loops ($recent_test_loops >= $MAX_CONSECUTIVE_TEST_LOOPS)"
|
|
@@ -810,6 +1034,7 @@ save_claude_session() {
|
|
|
810
1034
|
session_id=$(extract_session_id_from_output "$output_file" 2>/dev/null || echo "")
|
|
811
1035
|
if [[ -n "$session_id" && "$session_id" != "null" ]]; then
|
|
812
1036
|
echo "$session_id" > "$CLAUDE_SESSION_FILE"
|
|
1037
|
+
sync_ralph_session_with_driver "$session_id"
|
|
813
1038
|
log_status "INFO" "Saved session: ${session_id:0:20}..."
|
|
814
1039
|
fi
|
|
815
1040
|
fi
|
|
@@ -819,6 +1044,101 @@ save_claude_session() {
|
|
|
819
1044
|
# SESSION LIFECYCLE MANAGEMENT FUNCTIONS (Phase 1.2)
|
|
820
1045
|
# =============================================================================
|
|
821
1046
|
|
|
1047
|
+
write_active_ralph_session() {
|
|
1048
|
+
local session_id=$1
|
|
1049
|
+
local created_at=$2
|
|
1050
|
+
local last_used=${3:-$created_at}
|
|
1051
|
+
|
|
1052
|
+
jq -n \
|
|
1053
|
+
--arg session_id "$session_id" \
|
|
1054
|
+
--arg created_at "$created_at" \
|
|
1055
|
+
--arg last_used "$last_used" \
|
|
1056
|
+
'{
|
|
1057
|
+
session_id: $session_id,
|
|
1058
|
+
created_at: $created_at,
|
|
1059
|
+
last_used: $last_used
|
|
1060
|
+
}' > "$RALPH_SESSION_FILE"
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
write_inactive_ralph_session() {
|
|
1064
|
+
local reset_at=$1
|
|
1065
|
+
local reset_reason=$2
|
|
1066
|
+
|
|
1067
|
+
jq -n \
|
|
1068
|
+
--arg session_id "" \
|
|
1069
|
+
--arg reset_at "$reset_at" \
|
|
1070
|
+
--arg reset_reason "$reset_reason" \
|
|
1071
|
+
'{
|
|
1072
|
+
session_id: $session_id,
|
|
1073
|
+
reset_at: $reset_at,
|
|
1074
|
+
reset_reason: $reset_reason
|
|
1075
|
+
}' > "$RALPH_SESSION_FILE"
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
get_ralph_session_state() {
|
|
1079
|
+
if [[ ! -f "$RALPH_SESSION_FILE" ]]; then
|
|
1080
|
+
echo "missing"
|
|
1081
|
+
return 0
|
|
1082
|
+
fi
|
|
1083
|
+
|
|
1084
|
+
if ! jq empty "$RALPH_SESSION_FILE" 2>/dev/null; then
|
|
1085
|
+
echo "invalid"
|
|
1086
|
+
return 0
|
|
1087
|
+
fi
|
|
1088
|
+
|
|
1089
|
+
local session_id_type
|
|
1090
|
+
session_id_type=$(
|
|
1091
|
+
jq -r 'if has("session_id") then (.session_id | type) else "missing" end' \
|
|
1092
|
+
"$RALPH_SESSION_FILE" 2>/dev/null
|
|
1093
|
+
) || {
|
|
1094
|
+
echo "invalid"
|
|
1095
|
+
return 0
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
if [[ "$session_id_type" != "string" ]]; then
|
|
1099
|
+
echo "invalid"
|
|
1100
|
+
return 0
|
|
1101
|
+
fi
|
|
1102
|
+
|
|
1103
|
+
local session_id
|
|
1104
|
+
session_id=$(jq -r '.session_id' "$RALPH_SESSION_FILE" 2>/dev/null) || {
|
|
1105
|
+
echo "invalid"
|
|
1106
|
+
return 0
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
if [[ "$session_id" == "" ]]; then
|
|
1110
|
+
echo "inactive"
|
|
1111
|
+
return 0
|
|
1112
|
+
fi
|
|
1113
|
+
|
|
1114
|
+
local created_at_type
|
|
1115
|
+
created_at_type=$(
|
|
1116
|
+
jq -r 'if has("created_at") then (.created_at | type) else "missing" end' \
|
|
1117
|
+
"$RALPH_SESSION_FILE" 2>/dev/null
|
|
1118
|
+
) || {
|
|
1119
|
+
echo "invalid"
|
|
1120
|
+
return 0
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
if [[ "$created_at_type" != "string" ]]; then
|
|
1124
|
+
echo "invalid"
|
|
1125
|
+
return 0
|
|
1126
|
+
fi
|
|
1127
|
+
|
|
1128
|
+
local created_at
|
|
1129
|
+
created_at=$(jq -r '.created_at' "$RALPH_SESSION_FILE" 2>/dev/null) || {
|
|
1130
|
+
echo "invalid"
|
|
1131
|
+
return 0
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
if ! is_usable_ralph_session_created_at "$created_at"; then
|
|
1135
|
+
echo "invalid"
|
|
1136
|
+
return 0
|
|
1137
|
+
fi
|
|
1138
|
+
|
|
1139
|
+
echo "active"
|
|
1140
|
+
}
|
|
1141
|
+
|
|
822
1142
|
# Get current session ID from Ralph session file
|
|
823
1143
|
# Returns: session ID string or empty if not found
|
|
824
1144
|
get_session_id() {
|
|
@@ -840,6 +1160,65 @@ get_session_id() {
|
|
|
840
1160
|
return 0
|
|
841
1161
|
}
|
|
842
1162
|
|
|
1163
|
+
is_usable_ralph_session_created_at() {
|
|
1164
|
+
local created_at=$1
|
|
1165
|
+
if [[ -z "$created_at" || "$created_at" == "null" ]]; then
|
|
1166
|
+
return 1
|
|
1167
|
+
fi
|
|
1168
|
+
|
|
1169
|
+
local created_at_epoch
|
|
1170
|
+
created_at_epoch=$(parse_iso_to_epoch_strict "$created_at") || return 1
|
|
1171
|
+
|
|
1172
|
+
local now_epoch
|
|
1173
|
+
now_epoch=$(get_epoch_seconds)
|
|
1174
|
+
|
|
1175
|
+
[[ "$created_at_epoch" -le "$now_epoch" ]]
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
get_active_session_created_at() {
|
|
1179
|
+
if [[ "$(get_ralph_session_state)" != "active" ]]; then
|
|
1180
|
+
echo ""
|
|
1181
|
+
return 0
|
|
1182
|
+
fi
|
|
1183
|
+
|
|
1184
|
+
local created_at
|
|
1185
|
+
created_at=$(jq -r '.created_at // ""' "$RALPH_SESSION_FILE" 2>/dev/null)
|
|
1186
|
+
if [[ "$created_at" == "null" ]]; then
|
|
1187
|
+
created_at=""
|
|
1188
|
+
fi
|
|
1189
|
+
|
|
1190
|
+
if ! is_usable_ralph_session_created_at "$created_at"; then
|
|
1191
|
+
echo ""
|
|
1192
|
+
return 0
|
|
1193
|
+
fi
|
|
1194
|
+
|
|
1195
|
+
echo "$created_at"
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
sync_ralph_session_with_driver() {
|
|
1199
|
+
local driver_session_id=$1
|
|
1200
|
+
if [[ -z "$driver_session_id" || "$driver_session_id" == "null" ]]; then
|
|
1201
|
+
return 0
|
|
1202
|
+
fi
|
|
1203
|
+
|
|
1204
|
+
local ts
|
|
1205
|
+
ts=$(get_iso_timestamp)
|
|
1206
|
+
|
|
1207
|
+
if [[ "$(get_ralph_session_state)" == "active" ]]; then
|
|
1208
|
+
local current_session_id
|
|
1209
|
+
current_session_id=$(get_session_id)
|
|
1210
|
+
local current_created_at
|
|
1211
|
+
current_created_at=$(get_active_session_created_at)
|
|
1212
|
+
|
|
1213
|
+
if [[ "$current_session_id" == "$driver_session_id" && -n "$current_created_at" ]]; then
|
|
1214
|
+
write_active_ralph_session "$driver_session_id" "$current_created_at" "$ts"
|
|
1215
|
+
return 0
|
|
1216
|
+
fi
|
|
1217
|
+
fi
|
|
1218
|
+
|
|
1219
|
+
write_active_ralph_session "$driver_session_id" "$ts" "$ts"
|
|
1220
|
+
}
|
|
1221
|
+
|
|
843
1222
|
# Reset session with reason logging
|
|
844
1223
|
# Usage: reset_session "reason_for_reset"
|
|
845
1224
|
reset_session() {
|
|
@@ -849,20 +1228,7 @@ reset_session() {
|
|
|
849
1228
|
local reset_timestamp
|
|
850
1229
|
reset_timestamp=$(get_iso_timestamp)
|
|
851
1230
|
|
|
852
|
-
|
|
853
|
-
jq -n \
|
|
854
|
-
--arg session_id "" \
|
|
855
|
-
--arg created_at "" \
|
|
856
|
-
--arg last_used "" \
|
|
857
|
-
--arg reset_at "$reset_timestamp" \
|
|
858
|
-
--arg reset_reason "$reason" \
|
|
859
|
-
'{
|
|
860
|
-
session_id: $session_id,
|
|
861
|
-
created_at: $created_at,
|
|
862
|
-
last_used: $last_used,
|
|
863
|
-
reset_at: $reset_at,
|
|
864
|
-
reset_reason: $reset_reason
|
|
865
|
-
}' > "$RALPH_SESSION_FILE"
|
|
1231
|
+
write_inactive_ralph_session "$reset_timestamp" "$reason"
|
|
866
1232
|
|
|
867
1233
|
# Also clear the Claude session file for consistency
|
|
868
1234
|
rm -f "$CLAUDE_SESSION_FILE" 2>/dev/null
|
|
@@ -951,67 +1317,39 @@ init_session_tracking() {
|
|
|
951
1317
|
local ts
|
|
952
1318
|
ts=$(get_iso_timestamp)
|
|
953
1319
|
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
new_session_id=$(generate_session_id)
|
|
958
|
-
|
|
959
|
-
jq -n \
|
|
960
|
-
--arg session_id "$new_session_id" \
|
|
961
|
-
--arg created_at "$ts" \
|
|
962
|
-
--arg last_used "$ts" \
|
|
963
|
-
--arg reset_at "" \
|
|
964
|
-
--arg reset_reason "" \
|
|
965
|
-
'{
|
|
966
|
-
session_id: $session_id,
|
|
967
|
-
created_at: $created_at,
|
|
968
|
-
last_used: $last_used,
|
|
969
|
-
reset_at: $reset_at,
|
|
970
|
-
reset_reason: $reset_reason
|
|
971
|
-
}' > "$RALPH_SESSION_FILE"
|
|
972
|
-
|
|
973
|
-
log_status "INFO" "Initialized session tracking (session: $new_session_id)"
|
|
1320
|
+
local session_state
|
|
1321
|
+
session_state=$(get_ralph_session_state)
|
|
1322
|
+
if [[ "$session_state" == "active" ]]; then
|
|
974
1323
|
return 0
|
|
975
1324
|
fi
|
|
976
1325
|
|
|
977
|
-
|
|
978
|
-
if ! jq empty "$RALPH_SESSION_FILE" 2>/dev/null; then
|
|
1326
|
+
if [[ "$session_state" == "invalid" ]]; then
|
|
979
1327
|
log_status "WARN" "Corrupted session file detected, recreating..."
|
|
980
|
-
local new_session_id
|
|
981
|
-
new_session_id=$(generate_session_id)
|
|
982
|
-
|
|
983
|
-
jq -n \
|
|
984
|
-
--arg session_id "$new_session_id" \
|
|
985
|
-
--arg created_at "$ts" \
|
|
986
|
-
--arg last_used "$ts" \
|
|
987
|
-
--arg reset_at "$ts" \
|
|
988
|
-
--arg reset_reason "corrupted_file_recovery" \
|
|
989
|
-
'{
|
|
990
|
-
session_id: $session_id,
|
|
991
|
-
created_at: $created_at,
|
|
992
|
-
last_used: $last_used,
|
|
993
|
-
reset_at: $reset_at,
|
|
994
|
-
reset_reason: $reset_reason
|
|
995
|
-
}' > "$RALPH_SESSION_FILE"
|
|
996
1328
|
fi
|
|
1329
|
+
|
|
1330
|
+
local new_session_id
|
|
1331
|
+
new_session_id=$(generate_session_id)
|
|
1332
|
+
write_active_ralph_session "$new_session_id" "$ts" "$ts"
|
|
1333
|
+
|
|
1334
|
+
log_status "INFO" "Initialized session tracking (session: $new_session_id)"
|
|
997
1335
|
}
|
|
998
1336
|
|
|
999
1337
|
# Update last_used timestamp in session file (called on each loop iteration)
|
|
1000
1338
|
update_session_last_used() {
|
|
1001
|
-
if [[
|
|
1339
|
+
if [[ "$(get_ralph_session_state)" != "active" ]]; then
|
|
1002
1340
|
return 0
|
|
1003
1341
|
fi
|
|
1004
1342
|
|
|
1005
1343
|
local ts
|
|
1006
1344
|
ts=$(get_iso_timestamp)
|
|
1007
1345
|
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1346
|
+
local session_id
|
|
1347
|
+
session_id=$(get_session_id)
|
|
1348
|
+
local created_at
|
|
1349
|
+
created_at=$(get_active_session_created_at)
|
|
1012
1350
|
|
|
1013
|
-
if [[
|
|
1014
|
-
|
|
1351
|
+
if [[ -n "$session_id" && -n "$created_at" ]]; then
|
|
1352
|
+
write_active_ralph_session "$session_id" "$created_at" "$ts"
|
|
1015
1353
|
fi
|
|
1016
1354
|
}
|
|
1017
1355
|
|
|
@@ -1435,11 +1773,31 @@ loop_count=0
|
|
|
1435
1773
|
main() {
|
|
1436
1774
|
initialize_runtime_context
|
|
1437
1775
|
|
|
1438
|
-
|
|
1439
|
-
if [[ "${_CLI_ALLOWED_TOOLS:-}" == "true" ]] && ! validate_allowed_tools "$CLAUDE_ALLOWED_TOOLS"; then
|
|
1776
|
+
if ! validate_permission_denial_mode "$PERMISSION_DENIAL_MODE"; then
|
|
1440
1777
|
exit 1
|
|
1441
1778
|
fi
|
|
1442
1779
|
|
|
1780
|
+
if [[ "$(driver_name)" == "claude-code" ]]; then
|
|
1781
|
+
normalize_claude_permission_mode
|
|
1782
|
+
|
|
1783
|
+
if ! validate_claude_permission_mode "$CLAUDE_PERMISSION_MODE"; then
|
|
1784
|
+
exit 1
|
|
1785
|
+
fi
|
|
1786
|
+
fi
|
|
1787
|
+
|
|
1788
|
+
if driver_supports_tool_allowlist; then
|
|
1789
|
+
# Validate --allowed-tools now that platform-specific VALID_TOOL_PATTERNS are loaded
|
|
1790
|
+
if [[ "${_CLI_ALLOWED_TOOLS:-}" == "true" ]] && ! validate_allowed_tools "$CLAUDE_ALLOWED_TOOLS"; then
|
|
1791
|
+
exit 1
|
|
1792
|
+
fi
|
|
1793
|
+
else
|
|
1794
|
+
warn_if_allowed_tools_ignored
|
|
1795
|
+
fi
|
|
1796
|
+
|
|
1797
|
+
if [[ "${_CLI_ALLOWED_TOOLS:-}" == "true" ]] && ! driver_supports_tool_allowlist; then
|
|
1798
|
+
_CLI_ALLOWED_TOOLS=""
|
|
1799
|
+
fi
|
|
1800
|
+
|
|
1443
1801
|
log_status "SUCCESS" "🚀 Ralph loop starting with $DRIVER_DISPLAY_NAME"
|
|
1444
1802
|
log_status "INFO" "Max calls per hour: $MAX_CALLS_PER_HOUR"
|
|
1445
1803
|
log_status "INFO" "Logs: $LOG_DIR/ | Docs: $DOCS_DIR/ | Status: $STATUS_FILE"
|
|
@@ -1531,45 +1889,6 @@ main() {
|
|
|
1531
1889
|
# Check for graceful exit conditions
|
|
1532
1890
|
local exit_reason=$(should_exit_gracefully)
|
|
1533
1891
|
if [[ "$exit_reason" != "" ]]; then
|
|
1534
|
-
# Handle permission_denied specially (Issue #101)
|
|
1535
|
-
if [[ "$exit_reason" == "permission_denied" ]]; then
|
|
1536
|
-
log_status "ERROR" "🚫 Permission denied - halting loop"
|
|
1537
|
-
reset_session "permission_denied"
|
|
1538
|
-
update_status "$loop_count" "$(cat "$CALL_COUNT_FILE")" "permission_denied" "halted" "permission_denied"
|
|
1539
|
-
|
|
1540
|
-
# Display helpful guidance for resolving permission issues
|
|
1541
|
-
echo ""
|
|
1542
|
-
echo -e "${RED}╔════════════════════════════════════════════════════════════╗${NC}"
|
|
1543
|
-
echo -e "${RED}║ PERMISSION DENIED - Loop Halted ║${NC}"
|
|
1544
|
-
echo -e "${RED}╚════════════════════════════════════════════════════════════╝${NC}"
|
|
1545
|
-
echo ""
|
|
1546
|
-
echo -e "${YELLOW}$DRIVER_DISPLAY_NAME was denied permission to execute commands.${NC}"
|
|
1547
|
-
echo ""
|
|
1548
|
-
echo -e "${YELLOW}To fix this:${NC}"
|
|
1549
|
-
echo " 1. Edit .ralphrc and update ALLOWED_TOOLS to include the required tools"
|
|
1550
|
-
echo " 2. Common patterns:"
|
|
1551
|
-
echo " - Bash(npm *) - All npm commands"
|
|
1552
|
-
echo " - Bash(npm install) - Only npm install"
|
|
1553
|
-
echo " - Bash(pnpm *) - All pnpm commands"
|
|
1554
|
-
echo " - Bash(yarn *) - All yarn commands"
|
|
1555
|
-
echo ""
|
|
1556
|
-
echo -e "${YELLOW}After updating .ralphrc:${NC}"
|
|
1557
|
-
echo " bash .ralph/ralph_loop.sh --reset-session # Clear stale session state"
|
|
1558
|
-
echo " bmalph run # Restart the loop"
|
|
1559
|
-
echo ""
|
|
1560
|
-
|
|
1561
|
-
# Show current ALLOWED_TOOLS if .ralphrc exists
|
|
1562
|
-
if [[ -f ".ralphrc" ]]; then
|
|
1563
|
-
local current_tools=$(grep "^ALLOWED_TOOLS=" ".ralphrc" 2>/dev/null | cut -d= -f2- | tr -d '"')
|
|
1564
|
-
if [[ -n "$current_tools" ]]; then
|
|
1565
|
-
echo -e "${BLUE}Current ALLOWED_TOOLS:${NC} $current_tools"
|
|
1566
|
-
echo ""
|
|
1567
|
-
fi
|
|
1568
|
-
fi
|
|
1569
|
-
|
|
1570
|
-
break
|
|
1571
|
-
fi
|
|
1572
|
-
|
|
1573
1892
|
log_status "SUCCESS" "🏁 Graceful exit triggered: $exit_reason"
|
|
1574
1893
|
reset_session "project_complete"
|
|
1575
1894
|
update_status "$loop_count" "$(cat "$CALL_COUNT_FILE")" "graceful_exit" "completed" "$exit_reason"
|
|
@@ -1591,6 +1910,17 @@ main() {
|
|
|
1591
1910
|
local exec_result=$?
|
|
1592
1911
|
|
|
1593
1912
|
if [ $exec_result -eq 0 ]; then
|
|
1913
|
+
if consume_current_loop_permission_denial "$loop_count"; then
|
|
1914
|
+
if [[ "$PERMISSION_DENIAL_ACTION" == "halt" ]]; then
|
|
1915
|
+
break
|
|
1916
|
+
fi
|
|
1917
|
+
|
|
1918
|
+
# Brief pause between loops when the denial was recorded but
|
|
1919
|
+
# policy allows Ralph to continue.
|
|
1920
|
+
sleep 5
|
|
1921
|
+
continue
|
|
1922
|
+
fi
|
|
1923
|
+
|
|
1594
1924
|
update_status "$loop_count" "$(cat "$CALL_COUNT_FILE")" "completed" "success"
|
|
1595
1925
|
|
|
1596
1926
|
# Brief pause between successful executions
|
|
@@ -1676,7 +2006,7 @@ Options:
|
|
|
1676
2006
|
Modern CLI Options (Phase 1.1):
|
|
1677
2007
|
--output-format FORMAT Set driver output format: json or text (default: $CLAUDE_OUTPUT_FORMAT)
|
|
1678
2008
|
Note: --live mode requires JSON and will auto-switch
|
|
1679
|
-
--allowed-tools TOOLS
|
|
2009
|
+
--allowed-tools TOOLS Claude Code only. Ignored by codex, cursor, and copilot
|
|
1680
2010
|
--no-continue Disable session continuity across loops
|
|
1681
2011
|
--session-expiry HOURS Set session expiration time in hours (default: $CLAUDE_SESSION_EXPIRY_HOURS)
|
|
1682
2012
|
|
|
@@ -1722,6 +2052,8 @@ while [[ $# -gt 0 ]]; do
|
|
|
1722
2052
|
;;
|
|
1723
2053
|
-c|--calls)
|
|
1724
2054
|
MAX_CALLS_PER_HOUR="$2"
|
|
2055
|
+
_cli_MAX_CALLS_PER_HOUR="$MAX_CALLS_PER_HOUR"
|
|
2056
|
+
_CLI_MAX_CALLS_PER_HOUR=true
|
|
1725
2057
|
shift 2
|
|
1726
2058
|
;;
|
|
1727
2059
|
-p|--prompt)
|
|
@@ -1743,6 +2075,8 @@ while [[ $# -gt 0 ]]; do
|
|
|
1743
2075
|
;;
|
|
1744
2076
|
-v|--verbose)
|
|
1745
2077
|
VERBOSE_PROGRESS=true
|
|
2078
|
+
_cli_VERBOSE_PROGRESS="$VERBOSE_PROGRESS"
|
|
2079
|
+
_CLI_VERBOSE_PROGRESS=true
|
|
1746
2080
|
shift
|
|
1747
2081
|
;;
|
|
1748
2082
|
-l|--live)
|
|
@@ -1752,6 +2086,8 @@ while [[ $# -gt 0 ]]; do
|
|
|
1752
2086
|
-t|--timeout)
|
|
1753
2087
|
if [[ "$2" =~ ^[1-9][0-9]*$ ]] && [[ "$2" -le 120 ]]; then
|
|
1754
2088
|
CLAUDE_TIMEOUT_MINUTES="$2"
|
|
2089
|
+
_cli_CLAUDE_TIMEOUT_MINUTES="$CLAUDE_TIMEOUT_MINUTES"
|
|
2090
|
+
_CLI_CLAUDE_TIMEOUT_MINUTES=true
|
|
1755
2091
|
else
|
|
1756
2092
|
echo "Error: Timeout must be a positive integer between 1 and 120 minutes"
|
|
1757
2093
|
exit 1
|
|
@@ -1785,6 +2121,8 @@ while [[ $# -gt 0 ]]; do
|
|
|
1785
2121
|
--output-format)
|
|
1786
2122
|
if [[ "$2" == "json" || "$2" == "text" ]]; then
|
|
1787
2123
|
CLAUDE_OUTPUT_FORMAT="$2"
|
|
2124
|
+
_cli_CLAUDE_OUTPUT_FORMAT="$CLAUDE_OUTPUT_FORMAT"
|
|
2125
|
+
_CLI_CLAUDE_OUTPUT_FORMAT=true
|
|
1788
2126
|
else
|
|
1789
2127
|
echo "Error: --output-format must be 'json' or 'text'"
|
|
1790
2128
|
exit 1
|
|
@@ -1793,11 +2131,14 @@ while [[ $# -gt 0 ]]; do
|
|
|
1793
2131
|
;;
|
|
1794
2132
|
--allowed-tools)
|
|
1795
2133
|
CLAUDE_ALLOWED_TOOLS="$2"
|
|
2134
|
+
_cli_CLAUDE_ALLOWED_TOOLS="$2"
|
|
1796
2135
|
_CLI_ALLOWED_TOOLS=true
|
|
1797
2136
|
shift 2
|
|
1798
2137
|
;;
|
|
1799
2138
|
--no-continue)
|
|
1800
2139
|
CLAUDE_USE_CONTINUE=false
|
|
2140
|
+
_cli_CLAUDE_USE_CONTINUE="$CLAUDE_USE_CONTINUE"
|
|
2141
|
+
_CLI_SESSION_CONTINUITY=true
|
|
1801
2142
|
shift
|
|
1802
2143
|
;;
|
|
1803
2144
|
--session-expiry)
|
|
@@ -1806,6 +2147,8 @@ while [[ $# -gt 0 ]]; do
|
|
|
1806
2147
|
exit 1
|
|
1807
2148
|
fi
|
|
1808
2149
|
CLAUDE_SESSION_EXPIRY_HOURS="$2"
|
|
2150
|
+
_cli_CLAUDE_SESSION_EXPIRY_HOURS="$2"
|
|
2151
|
+
_CLI_SESSION_EXPIRY_HOURS=true
|
|
1809
2152
|
shift 2
|
|
1810
2153
|
;;
|
|
1811
2154
|
--auto-reset-circuit)
|