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 baff052a..21dd8878 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,11 +34,12 @@ 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" 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 @@ -48,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 @@ -68,41 +73,115 @@ 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" +# 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 "===============================================================" 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 + # 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 + TOOL_EXIT_CODE=0 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=$( (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 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=$( (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 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