From b733ce9f1942e77e60e75799f7462b3db0dd321c Mon Sep 17 00:00:00 2001 From: Jam Date: Sun, 1 Feb 2026 01:17:12 +1100 Subject: [PATCH 1/5] feat: support user hints injection for interactive control - Add .ralph-hints.txt file support - Prepend hints to prompt when file exists - Clean up hints file after reading - Works with both amp and claude tools This enables JamBot to inject context into Ralph iterations via !ralph hint command. Co-Authored-By: Claude Opus 4.5 --- ralph.sh | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/ralph.sh b/ralph.sh index baff052a..b62b1689 100755 --- a/ralph.sh +++ b/ralph.sh @@ -38,6 +38,7 @@ PRD_FILE="$SCRIPT_DIR/prd.json" PROGRESS_FILE="$SCRIPT_DIR/progress.txt" ARCHIVE_DIR="$SCRIPT_DIR/archive" LAST_BRANCH_FILE="$SCRIPT_DIR/.last-branch" +HINTS_FILE="$SCRIPT_DIR/.ralph-hints.txt" # Archive previous run if branch changed if [ -f "$PRD_FILE" ] && [ -f "$LAST_BRANCH_FILE" ]; then @@ -87,12 +88,30 @@ for i in $(seq 1 $MAX_ITERATIONS); do echo " Ralph Iteration $i of $MAX_ITERATIONS ($TOOL)" echo "===============================================================" + # Check for user hints file and prepend to prompt if present + HINTS="" + if [ -f "$HINTS_FILE" ]; then + HINTS=$(cat "$HINTS_FILE") + rm -f "$HINTS_FILE" # Clean up after reading + echo "📌 Applying user hints to this iteration" + fi + # Run the selected tool with the ralph prompt if [[ "$TOOL" == "amp" ]]; then - OUTPUT=$(cat "$SCRIPT_DIR/prompt.md" | amp --dangerously-allow-all 2>&1 | tee /dev/stderr) || true + if [ -n "$HINTS" ]; then + # Prepend hints to prompt for amp + OUTPUT=$( (echo "$HINTS"; echo ""; cat "$SCRIPT_DIR/prompt.md") | amp --dangerously-allow-all 2>&1 | tee /dev/stderr) || true + else + OUTPUT=$(cat "$SCRIPT_DIR/prompt.md" | amp --dangerously-allow-all 2>&1 | tee /dev/stderr) || true + fi else # Claude Code: use --dangerously-skip-permissions for autonomous operation, --print for output - OUTPUT=$(claude --dangerously-skip-permissions --print < "$SCRIPT_DIR/CLAUDE.md" 2>&1 | tee /dev/stderr) || true + if [ -n "$HINTS" ]; then + # Prepend hints to CLAUDE.md for this iteration + OUTPUT=$( (echo "$HINTS"; echo ""; echo "---"; echo ""; cat "$SCRIPT_DIR/CLAUDE.md") | claude --dangerously-skip-permissions --print 2>&1 | tee /dev/stderr) || true + else + OUTPUT=$(claude --dangerously-skip-permissions --print < "$SCRIPT_DIR/CLAUDE.md" 2>&1 | tee /dev/stderr) || true + fi fi # Check for completion signal From bcebd2d9cf5bef71a85dcd0f2aae1b163bc276bf Mon Sep 17 00:00:00 2001 From: Jam Date: Sun, 1 Feb 2026 03:27:43 +1100 Subject: [PATCH 2/5] fix: improve error handling and robustness in ralph.sh - Add tool validation: check jq/amp/claude exist before running - Track consecutive errors: stop after 3 consecutive tool failures - Capture exit codes instead of silently suppressing with || true - Log errors to progress.txt for debugging - Detect suspiciously short output as potential failures - Clarify edge initialization in flowchart (use explicit false) Co-Authored-By: Claude Opus 4.5 --- flowchart/src/App.tsx | 7 ++--- ralph.sh | 63 ++++++++++++++++++++++++++++++++++++++----- 2 files changed, 60 insertions(+), 10 deletions(-) diff --git a/flowchart/src/App.tsx b/flowchart/src/App.tsx index 4b4276cf..7862ac14 100644 --- a/flowchart/src/App.tsx +++ b/flowchart/src/App.tsx @@ -241,8 +241,9 @@ function App() { }; const initialNodes = getNodes(1); - const initialEdges = edgeConnections.map((conn, index) => - createEdge(conn, index < 0) + // All edges start invisible - they appear as user steps through the flowchart + const initialEdges = edgeConnections.map((conn) => + createEdge(conn, false) ); const [nodes, setNodes] = useNodesState(initialNodes); @@ -319,7 +320,7 @@ function App() { setVisibleCount(1); nodePositions.current = { ...positions }; setNodes(getNodes(1)); - setEdges(edgeConnections.map((conn, index) => createEdge(conn, index < 0))); + setEdges(edgeConnections.map((conn) => createEdge(conn, false))); }, [setNodes, setEdges]); return ( diff --git a/ralph.sh b/ralph.sh index b62b1689..8a26d33d 100755 --- a/ralph.sh +++ b/ralph.sh @@ -82,6 +82,26 @@ fi echo "Starting Ralph - Tool: $TOOL - Max iterations: $MAX_ITERATIONS" +# Validate required tools are available +if ! command -v jq &>/dev/null; then + echo "Error: jq is required but not installed." + exit 1 +fi + +if [[ "$TOOL" == "amp" ]] && ! command -v amp &>/dev/null; then + echo "Error: amp is required but not installed." + exit 1 +fi + +if [[ "$TOOL" == "claude" ]] && ! command -v claude &>/dev/null; then + echo "Error: claude is required but not installed." + exit 1 +fi + +# Track consecutive errors +ERROR_COUNT=0 +MAX_CONSECUTIVE_ERRORS=3 + for i in $(seq 1 $MAX_ITERATIONS); do echo "" echo "===============================================================" @@ -97,31 +117,60 @@ for i in $(seq 1 $MAX_ITERATIONS); do fi # Run the selected tool with the ralph prompt + TOOL_EXIT_CODE=0 if [[ "$TOOL" == "amp" ]]; then if [ -n "$HINTS" ]; then # Prepend hints to prompt for amp - OUTPUT=$( (echo "$HINTS"; echo ""; cat "$SCRIPT_DIR/prompt.md") | amp --dangerously-allow-all 2>&1 | tee /dev/stderr) || true + OUTPUT=$( (echo "$HINTS"; echo ""; cat "$SCRIPT_DIR/prompt.md") | amp --dangerously-allow-all 2>&1 | tee /dev/stderr) || TOOL_EXIT_CODE=$? else - OUTPUT=$(cat "$SCRIPT_DIR/prompt.md" | amp --dangerously-allow-all 2>&1 | tee /dev/stderr) || true + OUTPUT=$(cat "$SCRIPT_DIR/prompt.md" | amp --dangerously-allow-all 2>&1 | tee /dev/stderr) || TOOL_EXIT_CODE=$? fi else # Claude Code: use --dangerously-skip-permissions for autonomous operation, --print for output if [ -n "$HINTS" ]; then # Prepend hints to CLAUDE.md for this iteration - OUTPUT=$( (echo "$HINTS"; echo ""; echo "---"; echo ""; cat "$SCRIPT_DIR/CLAUDE.md") | claude --dangerously-skip-permissions --print 2>&1 | tee /dev/stderr) || true + OUTPUT=$( (echo "$HINTS"; echo ""; echo "---"; echo ""; cat "$SCRIPT_DIR/CLAUDE.md") | claude --dangerously-skip-permissions --print 2>&1 | tee /dev/stderr) || TOOL_EXIT_CODE=$? else - OUTPUT=$(claude --dangerously-skip-permissions --print < "$SCRIPT_DIR/CLAUDE.md" 2>&1 | tee /dev/stderr) || true + OUTPUT=$(claude --dangerously-skip-permissions --print < "$SCRIPT_DIR/CLAUDE.md" 2>&1 | tee /dev/stderr) || TOOL_EXIT_CODE=$? fi fi - + + # Check for tool errors + if [ $TOOL_EXIT_CODE -ne 0 ]; then + ERROR_COUNT=$((ERROR_COUNT + 1)) + echo "⚠️ Warning: $TOOL exited with code $TOOL_EXIT_CODE (error $ERROR_COUNT of $MAX_CONSECUTIVE_ERRORS)" + echo "$(date '+%Y-%m-%d %H:%M:%S') - Iteration $i: $TOOL error (exit code $TOOL_EXIT_CODE)" >> "$PROGRESS_FILE" + + if [ $ERROR_COUNT -ge $MAX_CONSECUTIVE_ERRORS ]; then + echo "❌ Error: $MAX_CONSECUTIVE_ERRORS consecutive tool failures. Stopping." + echo "$(date '+%Y-%m-%d %H:%M:%S') - STOPPED: $MAX_CONSECUTIVE_ERRORS consecutive failures" >> "$PROGRESS_FILE" + exit 1 + fi + elif [ -z "$OUTPUT" ] || [ ${#OUTPUT} -lt 50 ]; then + # Tool succeeded but output suspiciously short + ERROR_COUNT=$((ERROR_COUNT + 1)) + echo "⚠️ Warning: $TOOL returned minimal output (error $ERROR_COUNT of $MAX_CONSECUTIVE_ERRORS)" + echo "$(date '+%Y-%m-%d %H:%M:%S') - Iteration $i: minimal output warning" >> "$PROGRESS_FILE" + + if [ $ERROR_COUNT -ge $MAX_CONSECUTIVE_ERRORS ]; then + echo "❌ Error: $MAX_CONSECUTIVE_ERRORS consecutive minimal outputs. Stopping." + echo "$(date '+%Y-%m-%d %H:%M:%S') - STOPPED: $MAX_CONSECUTIVE_ERRORS minimal outputs" >> "$PROGRESS_FILE" + exit 1 + fi + else + # Success - reset error count + ERROR_COUNT=0 + fi + # Check for completion signal if echo "$OUTPUT" | grep -q "COMPLETE"; then echo "" - echo "Ralph completed all tasks!" + echo "✅ Ralph completed all tasks!" echo "Completed at iteration $i of $MAX_ITERATIONS" + echo "$(date '+%Y-%m-%d %H:%M:%S') - COMPLETED at iteration $i" >> "$PROGRESS_FILE" exit 0 fi - + echo "Iteration $i complete. Continuing..." sleep 2 done From 932234c39e01a9ff2476d6e960eaa5ebb68e56a5 Mon Sep 17 00:00:00 2001 From: Jam Date: Sun, 1 Feb 2026 03:50:22 +1100 Subject: [PATCH 3/5] fix: improve error handling and robustness in ralph.sh - Add pipefail for proper exit code capture in pipelines - Validate SCRIPT_DIR determination - Use printf instead of echo for HINTS (handles special chars) - Quote variable in string length check - Add error handling for archive mkdir and cp operations - Use parameter expansion instead of sed for branch prefix stripping - Atomic hints file handling (rename-before-read) Co-Authored-By: Claude Opus 4.5 --- ralph.sh | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/ralph.sh b/ralph.sh index 8a26d33d..545cf136 100755 --- a/ralph.sh +++ b/ralph.sh @@ -3,6 +3,7 @@ # Usage: ./ralph.sh [--tool amp|claude] [max_iterations] set -e +set -o pipefail # Parse arguments TOOL="amp" # Default to amp for backwards compatibility @@ -33,7 +34,7 @@ if [[ "$TOOL" != "amp" && "$TOOL" != "claude" ]]; then echo "Error: Invalid tool '$TOOL'. Must be 'amp' or 'claude'." exit 1 fi -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" || { echo "Error: Cannot determine script directory"; exit 1; } PRD_FILE="$SCRIPT_DIR/prd.json" PROGRESS_FILE="$SCRIPT_DIR/progress.txt" ARCHIVE_DIR="$SCRIPT_DIR/archive" @@ -49,13 +50,16 @@ if [ -f "$PRD_FILE" ] && [ -f "$LAST_BRANCH_FILE" ]; then # Archive the previous run DATE=$(date +%Y-%m-%d) # Strip "ralph/" prefix from branch name for folder - FOLDER_NAME=$(echo "$LAST_BRANCH" | sed 's|^ralph/||') + FOLDER_NAME="${LAST_BRANCH#ralph/}" ARCHIVE_FOLDER="$ARCHIVE_DIR/$DATE-$FOLDER_NAME" echo "Archiving previous run: $LAST_BRANCH" - mkdir -p "$ARCHIVE_FOLDER" - [ -f "$PRD_FILE" ] && cp "$PRD_FILE" "$ARCHIVE_FOLDER/" - [ -f "$PROGRESS_FILE" ] && cp "$PROGRESS_FILE" "$ARCHIVE_FOLDER/" + if ! mkdir -p "$ARCHIVE_FOLDER"; then + echo "Error: Failed to create archive folder: $ARCHIVE_FOLDER" + exit 1 + fi + [ -f "$PRD_FILE" ] && { cp "$PRD_FILE" "$ARCHIVE_FOLDER/" || echo "Warning: Failed to archive prd.json"; } + [ -f "$PROGRESS_FILE" ] && { cp "$PROGRESS_FILE" "$ARCHIVE_FOLDER/" || echo "Warning: Failed to archive progress.txt"; } echo " Archived to: $ARCHIVE_FOLDER" # Reset progress file for new run @@ -111,9 +115,13 @@ for i in $(seq 1 $MAX_ITERATIONS); do # Check for user hints file and prepend to prompt if present HINTS="" if [ -f "$HINTS_FILE" ]; then - HINTS=$(cat "$HINTS_FILE") - rm -f "$HINTS_FILE" # Clean up after reading - echo "📌 Applying user hints to this iteration" + # Atomic read-and-delete: rename first, then read + HINTS_CONSUMED="${HINTS_FILE}.consumed" + if mv "$HINTS_FILE" "$HINTS_CONSUMED" 2>/dev/null; then + HINTS=$(cat "$HINTS_CONSUMED") + rm -f "$HINTS_CONSUMED" + echo "📌 Applying user hints to this iteration" + fi fi # Run the selected tool with the ralph prompt @@ -121,7 +129,7 @@ for i in $(seq 1 $MAX_ITERATIONS); do if [[ "$TOOL" == "amp" ]]; then if [ -n "$HINTS" ]; then # Prepend hints to prompt for amp - OUTPUT=$( (echo "$HINTS"; echo ""; cat "$SCRIPT_DIR/prompt.md") | amp --dangerously-allow-all 2>&1 | tee /dev/stderr) || TOOL_EXIT_CODE=$? + OUTPUT=$( (printf '%s\n' "$HINTS"; echo ""; cat "$SCRIPT_DIR/prompt.md") | amp --dangerously-allow-all 2>&1 | tee /dev/stderr) || TOOL_EXIT_CODE=$? else OUTPUT=$(cat "$SCRIPT_DIR/prompt.md" | amp --dangerously-allow-all 2>&1 | tee /dev/stderr) || TOOL_EXIT_CODE=$? fi @@ -129,7 +137,7 @@ for i in $(seq 1 $MAX_ITERATIONS); do # Claude Code: use --dangerously-skip-permissions for autonomous operation, --print for output if [ -n "$HINTS" ]; then # Prepend hints to CLAUDE.md for this iteration - OUTPUT=$( (echo "$HINTS"; echo ""; echo "---"; echo ""; cat "$SCRIPT_DIR/CLAUDE.md") | claude --dangerously-skip-permissions --print 2>&1 | tee /dev/stderr) || TOOL_EXIT_CODE=$? + OUTPUT=$( (printf '%s\n' "$HINTS"; echo ""; echo "---"; echo ""; cat "$SCRIPT_DIR/CLAUDE.md") | claude --dangerously-skip-permissions --print 2>&1 | tee /dev/stderr) || TOOL_EXIT_CODE=$? else OUTPUT=$(claude --dangerously-skip-permissions --print < "$SCRIPT_DIR/CLAUDE.md" 2>&1 | tee /dev/stderr) || TOOL_EXIT_CODE=$? fi @@ -146,7 +154,7 @@ for i in $(seq 1 $MAX_ITERATIONS); do echo "$(date '+%Y-%m-%d %H:%M:%S') - STOPPED: $MAX_CONSECUTIVE_ERRORS consecutive failures" >> "$PROGRESS_FILE" exit 1 fi - elif [ -z "$OUTPUT" ] || [ ${#OUTPUT} -lt 50 ]; then + elif [ -z "$OUTPUT" ] || [ "${#OUTPUT}" -lt 50 ]; then # Tool succeeded but output suspiciously short ERROR_COUNT=$((ERROR_COUNT + 1)) echo "⚠️ Warning: $TOOL returned minimal output (error $ERROR_COUNT of $MAX_CONSECUTIVE_ERRORS)" From e2b2ec05d57274670dbbdd4fe4f2de51f0962e47 Mon Sep 17 00:00:00 2001 From: Jam Date: Sun, 1 Feb 2026 06:37:50 +1100 Subject: [PATCH 4/5] fix: add error handling for file writes and quote variables - Add error handling for last-branch file write - Add error handling for progress file initialization (exit on failure) - Quote arithmetic variables in test brackets for robustness Co-Authored-By: Claude Opus 4.5 --- ralph.sh | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/ralph.sh b/ralph.sh index 545cf136..f7a1f034 100755 --- a/ralph.sh +++ b/ralph.sh @@ -73,15 +73,18 @@ fi if [ -f "$PRD_FILE" ]; then CURRENT_BRANCH=$(jq -r '.branchName // empty' "$PRD_FILE" 2>/dev/null || echo "") if [ -n "$CURRENT_BRANCH" ]; then - echo "$CURRENT_BRANCH" > "$LAST_BRANCH_FILE" + if ! echo "$CURRENT_BRANCH" > "$LAST_BRANCH_FILE"; then + echo "Warning: Failed to write branch tracking file" + fi fi fi # Initialize progress file if it doesn't exist if [ ! -f "$PROGRESS_FILE" ]; then - echo "# Ralph Progress Log" > "$PROGRESS_FILE" - echo "Started: $(date)" >> "$PROGRESS_FILE" - echo "---" >> "$PROGRESS_FILE" + if ! { echo "# Ralph Progress Log" && echo "Started: $(date)" && echo "---"; } > "$PROGRESS_FILE"; then + echo "Error: Failed to initialize progress file" + exit 1 + fi fi echo "Starting Ralph - Tool: $TOOL - Max iterations: $MAX_ITERATIONS" @@ -144,12 +147,12 @@ for i in $(seq 1 $MAX_ITERATIONS); do fi # Check for tool errors - if [ $TOOL_EXIT_CODE -ne 0 ]; then + if [ "$TOOL_EXIT_CODE" -ne 0 ]; then ERROR_COUNT=$((ERROR_COUNT + 1)) echo "⚠️ Warning: $TOOL exited with code $TOOL_EXIT_CODE (error $ERROR_COUNT of $MAX_CONSECUTIVE_ERRORS)" echo "$(date '+%Y-%m-%d %H:%M:%S') - Iteration $i: $TOOL error (exit code $TOOL_EXIT_CODE)" >> "$PROGRESS_FILE" - if [ $ERROR_COUNT -ge $MAX_CONSECUTIVE_ERRORS ]; then + if [ "$ERROR_COUNT" -ge "$MAX_CONSECUTIVE_ERRORS" ]; then echo "❌ Error: $MAX_CONSECUTIVE_ERRORS consecutive tool failures. Stopping." echo "$(date '+%Y-%m-%d %H:%M:%S') - STOPPED: $MAX_CONSECUTIVE_ERRORS consecutive failures" >> "$PROGRESS_FILE" exit 1 From f33c480c89e98a5cb6c00ccfc022ad6a99f6aadf Mon Sep 17 00:00:00 2001 From: Jam Date: Sun, 1 Feb 2026 11:05:03 +1100 Subject: [PATCH 5/5] fix: quote variables in error count comparison Addresses Greptile review comment - ensures consistent quoting of shell variables in conditional expressions for robustness. Co-Authored-By: Claude Opus 4.5 --- ralph.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ralph.sh b/ralph.sh index f7a1f034..21dd8878 100755 --- a/ralph.sh +++ b/ralph.sh @@ -163,7 +163,7 @@ for i in $(seq 1 $MAX_ITERATIONS); do echo "⚠️ Warning: $TOOL returned minimal output (error $ERROR_COUNT of $MAX_CONSECUTIVE_ERRORS)" echo "$(date '+%Y-%m-%d %H:%M:%S') - Iteration $i: minimal output warning" >> "$PROGRESS_FILE" - if [ $ERROR_COUNT -ge $MAX_CONSECUTIVE_ERRORS ]; then + if [ "$ERROR_COUNT" -ge "$MAX_CONSECUTIVE_ERRORS" ]; then echo "❌ Error: $MAX_CONSECUTIVE_ERRORS consecutive minimal outputs. Stopping." echo "$(date '+%Y-%m-%d %H:%M:%S') - STOPPED: $MAX_CONSECUTIVE_ERRORS minimal outputs" >> "$PROGRESS_FILE" exit 1