bmalph 2.7.6 → 2.7.7
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 +4 -4
- package/dist/commands/run.js +11 -2
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/watch.js +5 -0
- package/dist/commands/watch.js.map +1 -1
- package/dist/installer/template-files.js +22 -3
- package/dist/installer/template-files.js.map +1 -1
- package/dist/run/run-dashboard.js +20 -6
- package/dist/run/run-dashboard.js.map +1 -1
- package/dist/transition/context.js +11 -3
- package/dist/transition/context.js.map +1 -1
- package/dist/watch/dashboard.js +25 -21
- package/dist/watch/dashboard.js.map +1 -1
- package/dist/watch/frame-writer.js +83 -0
- package/dist/watch/frame-writer.js.map +1 -0
- package/dist/watch/renderer.js +174 -51
- package/dist/watch/renderer.js.map +1 -1
- package/dist/watch/state-reader.js +8 -0
- package/dist/watch/state-reader.js.map +1 -1
- package/package.json +1 -1
- package/ralph/RALPH-REFERENCE.md +4 -4
- package/ralph/drivers/claude-code.sh +4 -2
- package/ralph/lib/enable_core.sh +10 -2
- package/ralph/lib/response_analyzer.sh +127 -35
- package/ralph/ralph_import.sh +9 -1
- package/ralph/ralph_loop.sh +88 -11
- package/ralph/templates/PROMPT.md +15 -5
- package/ralph/templates/ralphrc.template +3 -3
|
@@ -396,6 +396,68 @@ trim_shell_whitespace() {
|
|
|
396
396
|
printf '%s' "$value"
|
|
397
397
|
}
|
|
398
398
|
|
|
399
|
+
extract_ralph_status_block_json() {
|
|
400
|
+
local text=$1
|
|
401
|
+
local normalized="${text//$'\r'/}"
|
|
402
|
+
|
|
403
|
+
if [[ "$normalized" != *"---RALPH_STATUS---"* ]]; then
|
|
404
|
+
return 1
|
|
405
|
+
fi
|
|
406
|
+
|
|
407
|
+
local block="${normalized#*---RALPH_STATUS---}"
|
|
408
|
+
if [[ "$block" == "$normalized" ]]; then
|
|
409
|
+
return 1
|
|
410
|
+
fi
|
|
411
|
+
|
|
412
|
+
if [[ "$block" == *"---END_RALPH_STATUS---"* ]]; then
|
|
413
|
+
block="${block%%---END_RALPH_STATUS---*}"
|
|
414
|
+
fi
|
|
415
|
+
|
|
416
|
+
local status=""
|
|
417
|
+
local exit_signal="false"
|
|
418
|
+
local exit_signal_found="false"
|
|
419
|
+
local tasks_completed_this_loop=0
|
|
420
|
+
local line=""
|
|
421
|
+
local trimmed=""
|
|
422
|
+
local value=""
|
|
423
|
+
|
|
424
|
+
while IFS= read -r line; do
|
|
425
|
+
trimmed=$(trim_shell_whitespace "$line")
|
|
426
|
+
|
|
427
|
+
case "$trimmed" in
|
|
428
|
+
STATUS:*)
|
|
429
|
+
value=$(trim_shell_whitespace "${trimmed#STATUS:}")
|
|
430
|
+
[[ -n "$value" ]] && status="$value"
|
|
431
|
+
;;
|
|
432
|
+
EXIT_SIGNAL:*)
|
|
433
|
+
value=$(trim_shell_whitespace "${trimmed#EXIT_SIGNAL:}")
|
|
434
|
+
if [[ "$value" == "true" || "$value" == "false" ]]; then
|
|
435
|
+
exit_signal="$value"
|
|
436
|
+
exit_signal_found="true"
|
|
437
|
+
fi
|
|
438
|
+
;;
|
|
439
|
+
TASKS_COMPLETED_THIS_LOOP:*)
|
|
440
|
+
value=$(trim_shell_whitespace "${trimmed#TASKS_COMPLETED_THIS_LOOP:}")
|
|
441
|
+
if [[ "$value" =~ ^-?[0-9]+$ ]]; then
|
|
442
|
+
tasks_completed_this_loop=$value
|
|
443
|
+
fi
|
|
444
|
+
;;
|
|
445
|
+
esac
|
|
446
|
+
done <<< "$block"
|
|
447
|
+
|
|
448
|
+
jq -n \
|
|
449
|
+
--arg status "$status" \
|
|
450
|
+
--argjson exit_signal_found "$exit_signal_found" \
|
|
451
|
+
--argjson exit_signal "$exit_signal" \
|
|
452
|
+
--argjson tasks_completed_this_loop "$tasks_completed_this_loop" \
|
|
453
|
+
'{
|
|
454
|
+
status: $status,
|
|
455
|
+
exit_signal_found: $exit_signal_found,
|
|
456
|
+
exit_signal: $exit_signal,
|
|
457
|
+
tasks_completed_this_loop: $tasks_completed_this_loop
|
|
458
|
+
}'
|
|
459
|
+
}
|
|
460
|
+
|
|
399
461
|
# Parse JSON response and extract structured fields
|
|
400
462
|
# Creates .ralph/.json_parse_result with normalized analysis data
|
|
401
463
|
# Supports FIVE JSON formats:
|
|
@@ -466,35 +528,38 @@ parse_json_response() {
|
|
|
466
528
|
# Track whether EXIT_SIGNAL was explicitly provided (vs inferred from STATUS)
|
|
467
529
|
local exit_signal=$(jq -r -j '.exit_signal // false' "$output_file" 2>/dev/null)
|
|
468
530
|
local explicit_exit_signal_found=$(jq -r -j 'has("exit_signal")' "$output_file" 2>/dev/null)
|
|
531
|
+
local tasks_completed_this_loop=$(jq -r -j '.tasks_completed_this_loop // 0' "$output_file" 2>/dev/null)
|
|
532
|
+
if [[ ! "$tasks_completed_this_loop" =~ ^-?[0-9]+$ ]]; then
|
|
533
|
+
tasks_completed_this_loop=0
|
|
534
|
+
fi
|
|
469
535
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
fi
|
|
536
|
+
local result_text=""
|
|
537
|
+
if [[ "$has_result_field" == "true" ]]; then
|
|
538
|
+
result_text=$(jq -r -j '.result // ""' "$output_file" 2>/dev/null)
|
|
539
|
+
fi
|
|
540
|
+
|
|
541
|
+
local ralph_status_json=""
|
|
542
|
+
if [[ -n "$result_text" ]] && ralph_status_json=$(extract_ralph_status_block_json "$result_text" 2>/dev/null); then
|
|
543
|
+
local embedded_exit_signal_found
|
|
544
|
+
embedded_exit_signal_found=$(printf '%s' "$ralph_status_json" | jq -r -j '.exit_signal_found' 2>/dev/null)
|
|
545
|
+
local embedded_exit_sig
|
|
546
|
+
embedded_exit_sig=$(printf '%s' "$ralph_status_json" | jq -r -j '.exit_signal' 2>/dev/null)
|
|
547
|
+
local embedded_status
|
|
548
|
+
embedded_status=$(printf '%s' "$ralph_status_json" | jq -r -j '.status' 2>/dev/null)
|
|
549
|
+
local embedded_tasks_completed
|
|
550
|
+
embedded_tasks_completed=$(printf '%s' "$ralph_status_json" | jq -r -j '.tasks_completed_this_loop' 2>/dev/null)
|
|
551
|
+
|
|
552
|
+
if [[ "$embedded_tasks_completed" =~ ^-?[0-9]+$ ]]; then
|
|
553
|
+
tasks_completed_this_loop=$embedded_tasks_completed
|
|
554
|
+
fi
|
|
555
|
+
|
|
556
|
+
if [[ "$embedded_exit_signal_found" == "true" ]]; then
|
|
557
|
+
explicit_exit_signal_found="true"
|
|
558
|
+
exit_signal="$embedded_exit_sig"
|
|
559
|
+
[[ "${VERBOSE_PROGRESS:-}" == "true" ]] && echo "DEBUG: Extracted EXIT_SIGNAL=$embedded_exit_sig from .result RALPH_STATUS block" >&2
|
|
560
|
+
elif [[ "$embedded_status" == "COMPLETE" && "$explicit_exit_signal_found" != "true" ]]; then
|
|
561
|
+
exit_signal="true"
|
|
562
|
+
[[ "${VERBOSE_PROGRESS:-}" == "true" ]] && echo "DEBUG: Inferred EXIT_SIGNAL=true from .result STATUS=COMPLETE (no explicit EXIT_SIGNAL found)" >&2
|
|
498
563
|
fi
|
|
499
564
|
fi
|
|
500
565
|
|
|
@@ -640,6 +705,7 @@ parse_json_response() {
|
|
|
640
705
|
--argjson loop_number "$loop_number" \
|
|
641
706
|
--arg session_id "$session_id" \
|
|
642
707
|
--argjson confidence "$confidence" \
|
|
708
|
+
--argjson tasks_completed_this_loop "$tasks_completed_this_loop" \
|
|
643
709
|
--argjson has_permission_denials "$has_permission_denials" \
|
|
644
710
|
--argjson permission_denial_count "$permission_denial_count" \
|
|
645
711
|
--argjson denied_commands "$denied_commands_json" \
|
|
@@ -655,6 +721,7 @@ parse_json_response() {
|
|
|
655
721
|
loop_number: $loop_number,
|
|
656
722
|
session_id: $session_id,
|
|
657
723
|
confidence: $confidence,
|
|
724
|
+
tasks_completed_this_loop: $tasks_completed_this_loop,
|
|
658
725
|
has_permission_denials: $has_permission_denials,
|
|
659
726
|
permission_denial_count: $permission_denial_count,
|
|
660
727
|
denied_commands: $denied_commands,
|
|
@@ -687,6 +754,7 @@ analyze_response() {
|
|
|
687
754
|
local exit_signal=false
|
|
688
755
|
local work_summary=""
|
|
689
756
|
local files_modified=0
|
|
757
|
+
local tasks_completed_this_loop=0
|
|
690
758
|
|
|
691
759
|
# Read output file
|
|
692
760
|
if [[ ! -f "$output_file" ]]; then
|
|
@@ -712,6 +780,7 @@ analyze_response() {
|
|
|
712
780
|
is_stuck=$(jq -r -j '.is_stuck' "$json_parse_result_file" 2>/dev/null || echo "false")
|
|
713
781
|
work_summary=$(jq -r -j '.summary' "$json_parse_result_file" 2>/dev/null || echo "")
|
|
714
782
|
files_modified=$(jq -r -j '.files_modified' "$json_parse_result_file" 2>/dev/null || echo "0")
|
|
783
|
+
tasks_completed_this_loop=$(jq -r -j '.tasks_completed_this_loop // 0' "$json_parse_result_file" 2>/dev/null || echo "0")
|
|
715
784
|
local json_confidence=$(jq -r -j '.confidence' "$json_parse_result_file" 2>/dev/null || echo "0")
|
|
716
785
|
local session_id=$(jq -r -j '.session_id' "$json_parse_result_file" 2>/dev/null || echo "")
|
|
717
786
|
|
|
@@ -733,6 +802,10 @@ analyze_response() {
|
|
|
733
802
|
confidence_score=$((json_confidence + 50))
|
|
734
803
|
fi
|
|
735
804
|
|
|
805
|
+
if [[ ! "$tasks_completed_this_loop" =~ ^-?[0-9]+$ ]]; then
|
|
806
|
+
tasks_completed_this_loop=0
|
|
807
|
+
fi
|
|
808
|
+
|
|
736
809
|
# Check for file changes via git (supplements JSON data)
|
|
737
810
|
# Fix #141: Detect both uncommitted changes AND committed changes
|
|
738
811
|
if command -v git &>/dev/null && git rev-parse --git-dir >/dev/null 2>&1; then
|
|
@@ -784,6 +857,7 @@ analyze_response() {
|
|
|
784
857
|
--argjson files_modified "$files_modified" \
|
|
785
858
|
--argjson confidence_score "$confidence_score" \
|
|
786
859
|
--argjson exit_signal "$exit_signal" \
|
|
860
|
+
--argjson tasks_completed_this_loop "$tasks_completed_this_loop" \
|
|
787
861
|
--arg work_summary "$work_summary" \
|
|
788
862
|
--argjson output_length "$output_length" \
|
|
789
863
|
--argjson has_permission_denials "$has_permission_denials" \
|
|
@@ -802,6 +876,9 @@ analyze_response() {
|
|
|
802
876
|
files_modified: $files_modified,
|
|
803
877
|
confidence_score: $confidence_score,
|
|
804
878
|
exit_signal: $exit_signal,
|
|
879
|
+
tasks_completed_this_loop: $tasks_completed_this_loop,
|
|
880
|
+
fix_plan_completed_delta: 0,
|
|
881
|
+
has_progress_tracking_mismatch: false,
|
|
805
882
|
work_summary: $work_summary,
|
|
806
883
|
output_length: $output_length,
|
|
807
884
|
has_permission_denials: $has_permission_denials,
|
|
@@ -823,13 +900,23 @@ analyze_response() {
|
|
|
823
900
|
local explicit_exit_signal_found=false
|
|
824
901
|
|
|
825
902
|
# 1. Check for explicit structured output (if Claude follows schema)
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
local status
|
|
829
|
-
|
|
903
|
+
local ralph_status_json=""
|
|
904
|
+
if ralph_status_json=$(extract_ralph_status_block_json "$output_content" 2>/dev/null); then
|
|
905
|
+
local status
|
|
906
|
+
status=$(printf '%s' "$ralph_status_json" | jq -r -j '.status' 2>/dev/null)
|
|
907
|
+
local exit_sig_found
|
|
908
|
+
exit_sig_found=$(printf '%s' "$ralph_status_json" | jq -r -j '.exit_signal_found' 2>/dev/null)
|
|
909
|
+
local exit_sig
|
|
910
|
+
exit_sig=$(printf '%s' "$ralph_status_json" | jq -r -j '.exit_signal' 2>/dev/null)
|
|
911
|
+
local parsed_tasks_completed
|
|
912
|
+
parsed_tasks_completed=$(printf '%s' "$ralph_status_json" | jq -r -j '.tasks_completed_this_loop' 2>/dev/null)
|
|
913
|
+
|
|
914
|
+
if [[ "$parsed_tasks_completed" =~ ^-?[0-9]+$ ]]; then
|
|
915
|
+
tasks_completed_this_loop=$parsed_tasks_completed
|
|
916
|
+
fi
|
|
830
917
|
|
|
831
918
|
# If EXIT_SIGNAL is explicitly provided, respect it
|
|
832
|
-
if [[
|
|
919
|
+
if [[ "$exit_sig_found" == "true" ]]; then
|
|
833
920
|
explicit_exit_signal_found=true
|
|
834
921
|
if [[ "$exit_sig" == "true" ]]; then
|
|
835
922
|
has_completion_signal=true
|
|
@@ -1002,6 +1089,7 @@ analyze_response() {
|
|
|
1002
1089
|
--argjson files_modified "$files_modified" \
|
|
1003
1090
|
--argjson confidence_score "$confidence_score" \
|
|
1004
1091
|
--argjson exit_signal "$exit_signal" \
|
|
1092
|
+
--argjson tasks_completed_this_loop "$tasks_completed_this_loop" \
|
|
1005
1093
|
--arg work_summary "$work_summary" \
|
|
1006
1094
|
--argjson output_length "$output_length" \
|
|
1007
1095
|
--argjson has_permission_denials "$has_permission_denials" \
|
|
@@ -1020,6 +1108,9 @@ analyze_response() {
|
|
|
1020
1108
|
files_modified: $files_modified,
|
|
1021
1109
|
confidence_score: $confidence_score,
|
|
1022
1110
|
exit_signal: $exit_signal,
|
|
1111
|
+
tasks_completed_this_loop: $tasks_completed_this_loop,
|
|
1112
|
+
fix_plan_completed_delta: 0,
|
|
1113
|
+
has_progress_tracking_mismatch: false,
|
|
1023
1114
|
work_summary: $work_summary,
|
|
1024
1115
|
output_length: $output_length,
|
|
1025
1116
|
has_permission_denials: $has_permission_denials,
|
|
@@ -1049,6 +1140,7 @@ update_exit_signals() {
|
|
|
1049
1140
|
local loop_number=$(jq -r -j '.loop_number' "$analysis_file")
|
|
1050
1141
|
local has_progress=$(jq -r -j '.analysis.has_progress' "$analysis_file")
|
|
1051
1142
|
local has_permission_denials=$(jq -r -j '.analysis.has_permission_denials // false' "$analysis_file")
|
|
1143
|
+
local has_progress_tracking_mismatch=$(jq -r -j '.analysis.has_progress_tracking_mismatch // false' "$analysis_file")
|
|
1052
1144
|
|
|
1053
1145
|
# Read current exit signals
|
|
1054
1146
|
local signals=$(cat "$exit_signals_file" 2>/dev/null || echo '{"test_only_loops": [], "done_signals": [], "completion_indicators": []}')
|
|
@@ -1065,7 +1157,7 @@ update_exit_signals() {
|
|
|
1065
1157
|
|
|
1066
1158
|
# Permission denials are handled in the same loop, so they must not become
|
|
1067
1159
|
# completion state that can halt the next loop.
|
|
1068
|
-
if [[ "$has_permission_denials" != "true" && "$has_completion_signal" == "true" ]]; then
|
|
1160
|
+
if [[ "$has_permission_denials" != "true" && "$has_progress_tracking_mismatch" != "true" && "$has_completion_signal" == "true" ]]; then
|
|
1069
1161
|
signals=$(echo "$signals" | jq ".done_signals += [$loop_number]")
|
|
1070
1162
|
fi
|
|
1071
1163
|
|
|
@@ -1074,7 +1166,7 @@ update_exit_signals() {
|
|
|
1074
1166
|
# due to deterministic scoring (+50 for JSON format, +20 for result field).
|
|
1075
1167
|
# This caused premature exits after 5 loops. Now we respect Claude's explicit intent.
|
|
1076
1168
|
local exit_signal=$(jq -r -j '.analysis.exit_signal // false' "$analysis_file")
|
|
1077
|
-
if [[ "$has_permission_denials" != "true" && "$exit_signal" == "true" ]]; then
|
|
1169
|
+
if [[ "$has_permission_denials" != "true" && "$has_progress_tracking_mismatch" != "true" && "$exit_signal" == "true" ]]; then
|
|
1078
1170
|
signals=$(echo "$signals" | jq ".completion_indicators += [$loop_number]")
|
|
1079
1171
|
fi
|
|
1080
1172
|
|
package/ralph/ralph_import.sh
CHANGED
|
@@ -353,9 +353,17 @@ You are Ralph, an autonomous AI development agent working on a [PROJECT NAME] pr
|
|
|
353
353
|
- Search the codebase before assuming something isn't implemented
|
|
354
354
|
- Use subagents for expensive operations (file searching, analysis)
|
|
355
355
|
- Write comprehensive tests with clear documentation
|
|
356
|
-
-
|
|
356
|
+
- Toggle completed story checkboxes in @fix_plan.md without rewriting story lines
|
|
357
357
|
- Commit working changes with descriptive messages
|
|
358
358
|
|
|
359
|
+
## Progress Tracking (CRITICAL)
|
|
360
|
+
- Ralph tracks progress by counting story checkboxes in @fix_plan.md
|
|
361
|
+
- When you complete a story, change `- [ ]` to `- [x]` on that exact story line
|
|
362
|
+
- Do NOT remove, rewrite, or reorder story lines in @fix_plan.md
|
|
363
|
+
- Update the checkbox before committing so the monitor updates immediately
|
|
364
|
+
- Set `TASKS_COMPLETED_THIS_LOOP` to the exact number of story checkboxes toggled this loop
|
|
365
|
+
- Only valid values: 0 or 1
|
|
366
|
+
|
|
359
367
|
## 🧪 Testing Guidelines (CRITICAL)
|
|
360
368
|
- LIMIT testing to ~20% of your total effort per loop
|
|
361
369
|
- PRIORITIZE: Implementation > Documentation > Tests
|
package/ralph/ralph_loop.sh
CHANGED
|
@@ -73,13 +73,13 @@ _env_CB_AUTO_RESET="${CB_AUTO_RESET:-}"
|
|
|
73
73
|
MAX_CALLS_PER_HOUR="${MAX_CALLS_PER_HOUR:-100}"
|
|
74
74
|
VERBOSE_PROGRESS="${VERBOSE_PROGRESS:-false}"
|
|
75
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"
|
|
76
|
+
DEFAULT_CLAUDE_ALLOWED_TOOLS="Write,Read,Edit,MultiEdit,Glob,Grep,Task,TodoWrite,WebFetch,WebSearch,EnterPlanMode,ExitPlanMode,NotebookEdit,Bash"
|
|
77
77
|
DEFAULT_PERMISSION_DENIAL_MODE="continue"
|
|
78
78
|
|
|
79
79
|
# Modern Claude CLI configuration (Phase 1.1)
|
|
80
80
|
CLAUDE_OUTPUT_FORMAT="${CLAUDE_OUTPUT_FORMAT:-json}"
|
|
81
81
|
CLAUDE_ALLOWED_TOOLS="${CLAUDE_ALLOWED_TOOLS:-$DEFAULT_CLAUDE_ALLOWED_TOOLS}"
|
|
82
|
-
CLAUDE_PERMISSION_MODE="${CLAUDE_PERMISSION_MODE:-
|
|
82
|
+
CLAUDE_PERMISSION_MODE="${CLAUDE_PERMISSION_MODE:-bypassPermissions}"
|
|
83
83
|
CLAUDE_USE_CONTINUE="${CLAUDE_USE_CONTINUE:-true}"
|
|
84
84
|
PERMISSION_DENIAL_MODE="${PERMISSION_DENIAL_MODE:-$DEFAULT_PERMISSION_DENIAL_MODE}"
|
|
85
85
|
CLAUDE_SESSION_FILE="$RALPH_DIR/.claude_session_id" # Session ID persistence file
|
|
@@ -108,6 +108,8 @@ VALID_TOOL_PATTERNS=(
|
|
|
108
108
|
"WebFetch"
|
|
109
109
|
"WebSearch"
|
|
110
110
|
"AskUserQuestion"
|
|
111
|
+
"EnterPlanMode"
|
|
112
|
+
"ExitPlanMode"
|
|
111
113
|
"Bash"
|
|
112
114
|
"Bash(git *)"
|
|
113
115
|
"Bash(npm *)"
|
|
@@ -249,7 +251,7 @@ driver_supports_tool_allowlist() {
|
|
|
249
251
|
driver_permission_denial_help() {
|
|
250
252
|
echo " - Review the active driver's permission or approval settings."
|
|
251
253
|
echo " - ALLOWED_TOOLS in $RALPHRC_FILE only applies to the Claude Code driver."
|
|
252
|
-
echo " - Keep CLAUDE_PERMISSION_MODE=
|
|
254
|
+
echo " - Keep CLAUDE_PERMISSION_MODE=bypassPermissions for unattended Claude Code loops."
|
|
253
255
|
echo " - After updating permissions, reset the session and restart the loop."
|
|
254
256
|
}
|
|
255
257
|
|
|
@@ -530,7 +532,7 @@ validate_permission_denial_mode() {
|
|
|
530
532
|
|
|
531
533
|
normalize_claude_permission_mode() {
|
|
532
534
|
if [[ -z "${CLAUDE_PERMISSION_MODE:-}" ]]; then
|
|
533
|
-
CLAUDE_PERMISSION_MODE="
|
|
535
|
+
CLAUDE_PERMISSION_MODE="bypassPermissions"
|
|
534
536
|
fi
|
|
535
537
|
}
|
|
536
538
|
|
|
@@ -726,6 +728,74 @@ wait_for_reset() {
|
|
|
726
728
|
log_status "SUCCESS" "Rate limit reset! Ready for new calls."
|
|
727
729
|
}
|
|
728
730
|
|
|
731
|
+
count_fix_plan_checkboxes() {
|
|
732
|
+
local fix_plan_file="${1:-$RALPH_DIR/@fix_plan.md}"
|
|
733
|
+
local completed_items=0
|
|
734
|
+
local uncompleted_items=0
|
|
735
|
+
local total_items=0
|
|
736
|
+
|
|
737
|
+
if [[ -f "$fix_plan_file" ]]; then
|
|
738
|
+
uncompleted_items=$(grep -cE "^[[:space:]]*- \[ \]" "$fix_plan_file" 2>/dev/null || true)
|
|
739
|
+
[[ -z "$uncompleted_items" ]] && uncompleted_items=0
|
|
740
|
+
completed_items=$(grep -cE "^[[:space:]]*- \[[xX]\]" "$fix_plan_file" 2>/dev/null || true)
|
|
741
|
+
[[ -z "$completed_items" ]] && completed_items=0
|
|
742
|
+
fi
|
|
743
|
+
|
|
744
|
+
total_items=$((completed_items + uncompleted_items))
|
|
745
|
+
printf '%s %s %s\n' "$completed_items" "$uncompleted_items" "$total_items"
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
enforce_fix_plan_progress_tracking() {
|
|
749
|
+
local analysis_file=$1
|
|
750
|
+
local completed_before=$2
|
|
751
|
+
local completed_after=$3
|
|
752
|
+
|
|
753
|
+
if [[ ! -f "$analysis_file" ]]; then
|
|
754
|
+
return 0
|
|
755
|
+
fi
|
|
756
|
+
|
|
757
|
+
local claimed_tasks
|
|
758
|
+
claimed_tasks=$(jq -r '.analysis.tasks_completed_this_loop // 0' "$analysis_file" 2>/dev/null || echo "0")
|
|
759
|
+
if [[ ! "$claimed_tasks" =~ ^-?[0-9]+$ ]]; then
|
|
760
|
+
claimed_tasks=0
|
|
761
|
+
fi
|
|
762
|
+
|
|
763
|
+
local fix_plan_completed_delta=$((completed_after - completed_before))
|
|
764
|
+
local has_progress_tracking_mismatch=false
|
|
765
|
+
if [[ $claimed_tasks -ne $fix_plan_completed_delta || $claimed_tasks -gt 1 || $fix_plan_completed_delta -gt 1 || $fix_plan_completed_delta -lt 0 ]]; then
|
|
766
|
+
has_progress_tracking_mismatch=true
|
|
767
|
+
fi
|
|
768
|
+
|
|
769
|
+
local tmp_file="$analysis_file.tmp"
|
|
770
|
+
if jq \
|
|
771
|
+
--argjson claimed_tasks "$claimed_tasks" \
|
|
772
|
+
--argjson fix_plan_completed_delta "$fix_plan_completed_delta" \
|
|
773
|
+
--argjson has_progress_tracking_mismatch "$has_progress_tracking_mismatch" \
|
|
774
|
+
'
|
|
775
|
+
(.analysis //= {}) |
|
|
776
|
+
.analysis.tasks_completed_this_loop = $claimed_tasks |
|
|
777
|
+
.analysis.fix_plan_completed_delta = $fix_plan_completed_delta |
|
|
778
|
+
.analysis.has_progress_tracking_mismatch = $has_progress_tracking_mismatch |
|
|
779
|
+
if $has_progress_tracking_mismatch then
|
|
780
|
+
.analysis.has_completion_signal = false |
|
|
781
|
+
.analysis.exit_signal = false
|
|
782
|
+
else
|
|
783
|
+
.
|
|
784
|
+
end
|
|
785
|
+
' "$analysis_file" > "$tmp_file" 2>/dev/null; then
|
|
786
|
+
mv "$tmp_file" "$analysis_file"
|
|
787
|
+
else
|
|
788
|
+
rm -f "$tmp_file" 2>/dev/null
|
|
789
|
+
return 0
|
|
790
|
+
fi
|
|
791
|
+
|
|
792
|
+
if [[ "$has_progress_tracking_mismatch" == "true" ]]; then
|
|
793
|
+
log_status "WARN" "Progress tracking mismatch: claimed $claimed_tasks completed task(s) but checkbox delta was $fix_plan_completed_delta. Completion signals suppressed for this loop."
|
|
794
|
+
fi
|
|
795
|
+
|
|
796
|
+
return 0
|
|
797
|
+
}
|
|
798
|
+
|
|
729
799
|
# Check if we should gracefully exit
|
|
730
800
|
should_exit_gracefully() {
|
|
731
801
|
|
|
@@ -792,11 +862,10 @@ should_exit_gracefully() {
|
|
|
792
862
|
# Fix #144: Only match valid markdown checkboxes, not date entries like [2026-01-29]
|
|
793
863
|
# Valid patterns: "- [ ]" (uncompleted) and "- [x]" or "- [X]" (completed)
|
|
794
864
|
if [[ -f "$RALPH_DIR/@fix_plan.md" ]]; then
|
|
795
|
-
local
|
|
796
|
-
|
|
797
|
-
local
|
|
798
|
-
|
|
799
|
-
local total_items=$((uncompleted_items + completed_items))
|
|
865
|
+
local completed_items=0
|
|
866
|
+
local uncompleted_items=0
|
|
867
|
+
local total_items=0
|
|
868
|
+
read -r completed_items uncompleted_items total_items < <(count_fix_plan_checkboxes "$RALPH_DIR/@fix_plan.md")
|
|
800
869
|
|
|
801
870
|
if [[ $total_items -gt 0 ]] && [[ $completed_items -eq $total_items ]]; then
|
|
802
871
|
log_status "WARN" "Exit condition: All @fix_plan.md items completed ($completed_items/$total_items)" >&2
|
|
@@ -901,8 +970,10 @@ build_loop_context() {
|
|
|
901
970
|
# Extract incomplete tasks from @fix_plan.md
|
|
902
971
|
# Bug #3 Fix: Support indented markdown checkboxes with [[:space:]]* pattern
|
|
903
972
|
if [[ -f "$RALPH_DIR/@fix_plan.md" ]]; then
|
|
904
|
-
local
|
|
905
|
-
|
|
973
|
+
local completed_tasks=0
|
|
974
|
+
local incomplete_tasks=0
|
|
975
|
+
local total_tasks=0
|
|
976
|
+
read -r completed_tasks incomplete_tasks total_tasks < <(count_fix_plan_checkboxes "$RALPH_DIR/@fix_plan.md")
|
|
906
977
|
context+="Remaining tasks: ${incomplete_tasks}. "
|
|
907
978
|
fi
|
|
908
979
|
|
|
@@ -1410,6 +1481,8 @@ execute_claude_code() {
|
|
|
1410
1481
|
local loop_count=$1
|
|
1411
1482
|
local calls_made=$(cat "$CALL_COUNT_FILE" 2>/dev/null || echo "0")
|
|
1412
1483
|
calls_made=$((calls_made + 1))
|
|
1484
|
+
local fix_plan_completed_before=0
|
|
1485
|
+
read -r fix_plan_completed_before _ _ < <(count_fix_plan_checkboxes "$RALPH_DIR/@fix_plan.md")
|
|
1413
1486
|
|
|
1414
1487
|
# Fix #141: Capture git HEAD SHA at loop start to detect commits as progress
|
|
1415
1488
|
# Store in file for access by progress detection after Claude execution
|
|
@@ -1664,6 +1737,10 @@ EOF
|
|
|
1664
1737
|
analyze_response "$output_file" "$loop_count"
|
|
1665
1738
|
local analysis_exit_code=$?
|
|
1666
1739
|
|
|
1740
|
+
local fix_plan_completed_after=0
|
|
1741
|
+
read -r fix_plan_completed_after _ _ < <(count_fix_plan_checkboxes "$RALPH_DIR/@fix_plan.md")
|
|
1742
|
+
enforce_fix_plan_progress_tracking "$RESPONSE_ANALYSIS_FILE" "$fix_plan_completed_before" "$fix_plan_completed_after"
|
|
1743
|
+
|
|
1667
1744
|
# Update exit signals based on analysis
|
|
1668
1745
|
update_exit_signals
|
|
1669
1746
|
|
|
@@ -9,16 +9,24 @@ You are Ralph, an autonomous AI development agent working on a [YOUR PROJECT NAM
|
|
|
9
9
|
3. Implement the highest priority item using best practices
|
|
10
10
|
4. Use parallel subagents for complex tasks (max 100 concurrent)
|
|
11
11
|
5. Run tests after each implementation
|
|
12
|
-
6. Update documentation and @fix_plan.md
|
|
12
|
+
6. Update documentation and the completed story checkbox in @fix_plan.md
|
|
13
13
|
|
|
14
14
|
## Key Principles
|
|
15
15
|
- ONE task per loop - focus on the most important thing
|
|
16
16
|
- Search the codebase before assuming something isn't implemented
|
|
17
17
|
- Use subagents for expensive operations (file searching, analysis)
|
|
18
18
|
- Write comprehensive tests with clear documentation
|
|
19
|
-
-
|
|
19
|
+
- Toggle completed story checkboxes in .ralph/@fix_plan.md without rewriting story lines
|
|
20
20
|
- Commit working changes with descriptive messages
|
|
21
21
|
|
|
22
|
+
## Progress Tracking (CRITICAL)
|
|
23
|
+
- Ralph tracks progress by counting story checkboxes in .ralph/@fix_plan.md
|
|
24
|
+
- When you complete a story, change `- [ ]` to `- [x]` on that exact story line
|
|
25
|
+
- Do NOT remove, rewrite, or reorder story lines in .ralph/@fix_plan.md
|
|
26
|
+
- Update the checkbox before committing so the monitor updates immediately
|
|
27
|
+
- Set `TASKS_COMPLETED_THIS_LOOP` to the exact number of story checkboxes toggled this loop
|
|
28
|
+
- Only valid values: 0 or 1
|
|
29
|
+
|
|
22
30
|
## 🧪 Testing Guidelines (CRITICAL)
|
|
23
31
|
- LIMIT testing to ~20% of your total effort per loop
|
|
24
32
|
- PRIORITIZE: Implementation > Documentation > Tests
|
|
@@ -34,6 +42,8 @@ You are Ralph, an autonomous AI development agent working on a [YOUR PROJECT NAM
|
|
|
34
42
|
- Keep .ralph/@AGENT.md updated with build/run instructions
|
|
35
43
|
- Document the WHY behind tests and implementations
|
|
36
44
|
- No placeholder implementations - build it properly
|
|
45
|
+
|
|
46
|
+
## Autonomous Mode (CRITICAL)
|
|
37
47
|
- do not ask the user questions during loop execution
|
|
38
48
|
- do not use AskUserQuestion, EnterPlanMode, or ExitPlanMode during loop execution
|
|
39
49
|
- make the safest reasonable assumption and continue
|
|
@@ -47,7 +57,7 @@ You are Ralph, an autonomous AI development agent working on a [YOUR PROJECT NAM
|
|
|
47
57
|
```
|
|
48
58
|
---RALPH_STATUS---
|
|
49
59
|
STATUS: IN_PROGRESS | COMPLETE | BLOCKED
|
|
50
|
-
TASKS_COMPLETED_THIS_LOOP:
|
|
60
|
+
TASKS_COMPLETED_THIS_LOOP: 0 | 1
|
|
51
61
|
FILES_MODIFIED: <number>
|
|
52
62
|
TESTS_STATUS: PASSING | FAILING | NOT_RUN
|
|
53
63
|
WORK_TYPE: IMPLEMENTATION | TESTING | DOCUMENTATION | REFACTORING
|
|
@@ -71,7 +81,7 @@ Set EXIT_SIGNAL to **true** when ALL of these conditions are met:
|
|
|
71
81
|
```
|
|
72
82
|
---RALPH_STATUS---
|
|
73
83
|
STATUS: IN_PROGRESS
|
|
74
|
-
TASKS_COMPLETED_THIS_LOOP:
|
|
84
|
+
TASKS_COMPLETED_THIS_LOOP: 1
|
|
75
85
|
FILES_MODIFIED: 5
|
|
76
86
|
TESTS_STATUS: PASSING
|
|
77
87
|
WORK_TYPE: IMPLEMENTATION
|
|
@@ -234,7 +244,7 @@ RECOMMENDATION: No remaining work, all .ralph/specs implemented
|
|
|
234
244
|
```
|
|
235
245
|
---RALPH_STATUS---
|
|
236
246
|
STATUS: IN_PROGRESS
|
|
237
|
-
TASKS_COMPLETED_THIS_LOOP:
|
|
247
|
+
TASKS_COMPLETED_THIS_LOOP: 1
|
|
238
248
|
FILES_MODIFIED: 7
|
|
239
249
|
TESTS_STATUS: PASSING
|
|
240
250
|
WORK_TYPE: IMPLEMENTATION
|
|
@@ -43,11 +43,11 @@ CLAUDE_OUTPUT_FORMAT="json"
|
|
|
43
43
|
# Comma-separated list of allowed tools for Claude Code only.
|
|
44
44
|
# Ignored by the codex, cursor, and copilot drivers.
|
|
45
45
|
# Opt in to interactive pauses by adding AskUserQuestion manually.
|
|
46
|
-
ALLOWED_TOOLS="Write,Read,Edit,MultiEdit,Glob,Grep,Task,TodoWrite,WebFetch,WebSearch,NotebookEdit,Bash"
|
|
46
|
+
ALLOWED_TOOLS="Write,Read,Edit,MultiEdit,Glob,Grep,Task,TodoWrite,WebFetch,WebSearch,EnterPlanMode,ExitPlanMode,NotebookEdit,Bash"
|
|
47
47
|
|
|
48
|
-
# Permission mode for Claude Code CLI (default:
|
|
48
|
+
# Permission mode for Claude Code CLI (default: bypassPermissions)
|
|
49
49
|
# Options: auto, acceptEdits, bypassPermissions, default, dontAsk, plan
|
|
50
|
-
CLAUDE_PERMISSION_MODE="
|
|
50
|
+
CLAUDE_PERMISSION_MODE="bypassPermissions"
|
|
51
51
|
|
|
52
52
|
# How Ralph responds when a driver reports permission denials:
|
|
53
53
|
# - continue: log the denial and keep looping (default for unattended mode)
|