@pageai/ralph-loop 1.0.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.
- package/.agent/PROMPT.md +58 -0
- package/.agent/STEERING.md +3 -0
- package/.agent/logs/LOG.md +13 -0
- package/.agent/prd/.gitkeep +0 -0
- package/.agent/screenshots/.gitkeep +0 -0
- package/.agent/skills/component-refactoring/SKILL.md +247 -0
- package/.agent/skills/component-refactoring/references/complexity-patterns.md +485 -0
- package/.agent/skills/component-refactoring/references/component-splitting.md +419 -0
- package/.agent/skills/component-refactoring/references/hook-extraction.md +317 -0
- package/.agent/skills/e2e-tester/SKILL.md +595 -0
- package/.agent/skills/frontend-code-review/SKILL.md +73 -0
- package/.agent/skills/frontend-code-review/references/code-quality.md +28 -0
- package/.agent/skills/frontend-code-review/references/performance.md +36 -0
- package/.agent/skills/frontend-testing/SKILL.md +316 -0
- package/.agent/skills/frontend-testing/assets/component-test.template.tsx +293 -0
- package/.agent/skills/frontend-testing/assets/hook-test.template.ts +207 -0
- package/.agent/skills/frontend-testing/assets/utility-test.template.ts +154 -0
- package/.agent/skills/frontend-testing/references/async-testing.md +345 -0
- package/.agent/skills/frontend-testing/references/checklist.md +188 -0
- package/.agent/skills/frontend-testing/references/common-patterns.md +449 -0
- package/.agent/skills/frontend-testing/references/mocking.md +289 -0
- package/.agent/skills/frontend-testing/references/workflow.md +265 -0
- package/.agent/skills/prd-creator/JSON.md +613 -0
- package/.agent/skills/prd-creator/PRD.md +196 -0
- package/.agent/skills/prd-creator/SKILL.md +143 -0
- package/.agent/skills/skill-creator/SKILL.md +355 -0
- package/.agent/skills/skill-creator/references/output-patterns.md +86 -0
- package/.agent/skills/skill-creator/references/workflows.md +28 -0
- package/.agent/skills/skill-creator/scripts/init_skill.py +300 -0
- package/.agent/skills/skill-creator/scripts/package_skill.py +110 -0
- package/.agent/skills/vercel-react-best-practices/AGENTS.md +2249 -0
- package/.agent/skills/vercel-react-best-practices/SKILL.md +125 -0
- package/.agent/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/.agent/skills/vercel-react-best-practices/rules/advanced-use-latest.md +49 -0
- package/.agent/skills/vercel-react-best-practices/rules/async-api-routes.md +38 -0
- package/.agent/skills/vercel-react-best-practices/rules/async-defer-await.md +80 -0
- package/.agent/skills/vercel-react-best-practices/rules/async-dependencies.md +36 -0
- package/.agent/skills/vercel-react-best-practices/rules/async-parallel.md +28 -0
- package/.agent/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/.agent/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/.agent/skills/vercel-react-best-practices/rules/bundle-conditional.md +31 -0
- package/.agent/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/.agent/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/.agent/skills/vercel-react-best-practices/rules/bundle-preload.md +50 -0
- package/.agent/skills/vercel-react-best-practices/rules/client-event-listeners.md +74 -0
- package/.agent/skills/vercel-react-best-practices/rules/client-swr-dedup.md +56 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +82 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-cache-function-results.md +80 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-cache-property-access.md +28 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-cache-storage.md +70 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-combine-iterations.md +32 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-early-exit.md +50 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-index-maps.md +37 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-length-check-first.md +49 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-min-max-loop.md +82 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/.agent/skills/vercel-react-best-practices/rules/rendering-activity.md +26 -0
- package/.agent/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/.agent/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/.agent/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/.agent/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/.agent/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/.agent/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/.agent/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/.agent/skills/vercel-react-best-practices/rules/rerender-dependencies.md +45 -0
- package/.agent/skills/vercel-react-best-practices/rules/rerender-derived-state.md +29 -0
- package/.agent/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/.agent/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/.agent/skills/vercel-react-best-practices/rules/rerender-memo.md +44 -0
- package/.agent/skills/vercel-react-best-practices/rules/rerender-transitions.md +40 -0
- package/.agent/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/.agent/skills/vercel-react-best-practices/rules/server-cache-lru.md +41 -0
- package/.agent/skills/vercel-react-best-practices/rules/server-cache-react.md +26 -0
- package/.agent/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +79 -0
- package/.agent/skills/vercel-react-best-practices/rules/server-serialization.md +38 -0
- package/.agent/skills/vitest-best-practices/AGENTS.md +84 -0
- package/.agent/skills/vitest-best-practices/SKILL.md +130 -0
- package/.agent/skills/vitest-best-practices/references/aaa-pattern.md +260 -0
- package/.agent/skills/vitest-best-practices/references/assertions.md +393 -0
- package/.agent/skills/vitest-best-practices/references/async-testing.md +454 -0
- package/.agent/skills/vitest-best-practices/references/error-handling.md +382 -0
- package/.agent/skills/vitest-best-practices/references/organization.md +212 -0
- package/.agent/skills/vitest-best-practices/references/parameterized-tests.md +297 -0
- package/.agent/skills/vitest-best-practices/references/performance.md +528 -0
- package/.agent/skills/vitest-best-practices/references/snapshot-testing.md +483 -0
- package/.agent/skills/vitest-best-practices/references/test-doubles.md +499 -0
- package/.agent/skills/vitest-best-practices/references/vitest-features.md +529 -0
- package/.agent/skills/web-design-guidelines/SKILL.md +39 -0
- package/.agent/tasks/.gitkeep +0 -0
- package/.agent/tasks.json +1 -0
- package/.claude/agents/code-reviewer.md +172 -0
- package/.claude/commands/aw.md +50 -0
- package/.claude/hooks/play-sound.js +87 -0
- package/.claude/hooks/pre-tool-use.js +40 -0
- package/.claude/settings.json +54 -0
- package/.claude/settings.local.json +13 -0
- package/.mcp.json +31 -0
- package/AGENTS.md +44 -0
- package/CLAUDE.md +1 -0
- package/README.md +236 -0
- package/bin/cli.js +156 -0
- package/bin/lib/copy.js +149 -0
- package/bin/lib/display.js +137 -0
- package/package.json +65 -0
- package/ralph.sh +333 -0
- package/scripts/lib/args.sh +44 -0
- package/scripts/lib/cleanup.sh +53 -0
- package/scripts/lib/constants.sh +25 -0
- package/scripts/lib/display.sh +196 -0
- package/scripts/lib/logging.sh +30 -0
- package/scripts/lib/notify.sh +41 -0
- package/scripts/lib/output.sh +147 -0
- package/scripts/lib/preflight.sh +57 -0
- package/scripts/lib/preview.sh +77 -0
- package/scripts/lib/promise.sh +76 -0
- package/scripts/lib/spinner.sh +85 -0
- package/scripts/lib/terminal.sh +57 -0
- package/scripts/lib/timing.sh +223 -0
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pageai/ralph-loop",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"description": "A long-running AI agent loop. Ralph automates software development tasks by iteratively working through a task list until completion.",
|
|
8
|
+
"main": "bin/cli.js",
|
|
9
|
+
"bin": {
|
|
10
|
+
"ralph-loop": "bin/cli.js"
|
|
11
|
+
},
|
|
12
|
+
"directories": {
|
|
13
|
+
"test": "tests"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"bin/",
|
|
20
|
+
"scripts/",
|
|
21
|
+
"ralph.sh",
|
|
22
|
+
"CLAUDE.md",
|
|
23
|
+
"AGENTS.md",
|
|
24
|
+
".mcp.json",
|
|
25
|
+
".agent/PROMPT.md",
|
|
26
|
+
".agent/STEERING.md",
|
|
27
|
+
".agent/tasks.json",
|
|
28
|
+
".agent/history/.gitignore",
|
|
29
|
+
".agent/logs/",
|
|
30
|
+
".agent/prd/",
|
|
31
|
+
".agent/screenshots/",
|
|
32
|
+
".agent/skills/",
|
|
33
|
+
".agent/tasks/",
|
|
34
|
+
".claude/",
|
|
35
|
+
".codex/",
|
|
36
|
+
".cursor/",
|
|
37
|
+
".agents/"
|
|
38
|
+
],
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "git+https://github.com/pageai-pro/ralph-loop.git"
|
|
42
|
+
},
|
|
43
|
+
"keywords": [
|
|
44
|
+
"ralph",
|
|
45
|
+
"ralph-loop",
|
|
46
|
+
"ralph-wiggum-loop",
|
|
47
|
+
"agentic-ai",
|
|
48
|
+
"ai",
|
|
49
|
+
"ai-agent",
|
|
50
|
+
"cli",
|
|
51
|
+
"claude-code",
|
|
52
|
+
"cc",
|
|
53
|
+
"openai",
|
|
54
|
+
"codex",
|
|
55
|
+
"gemini",
|
|
56
|
+
"ampcode",
|
|
57
|
+
"ralph-plugin"
|
|
58
|
+
],
|
|
59
|
+
"author": "PageAI",
|
|
60
|
+
"license": "MIT",
|
|
61
|
+
"bugs": {
|
|
62
|
+
"url": "https://github.com/pageai-pro/ralph-loop/issues"
|
|
63
|
+
},
|
|
64
|
+
"homepage": "https://github.com/pageai-pro/ralph-loop#readme"
|
|
65
|
+
}
|
package/ralph.sh
ADDED
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Ralph Wiggum - Long-running AI agent loop
|
|
3
|
+
# Usage: ./ralph.sh [--help] [--once] [--max-iterations N] [N]
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
8
|
+
cd "$SCRIPT_DIR"
|
|
9
|
+
|
|
10
|
+
PRD_FILE="$SCRIPT_DIR/.agent/prd/PRD.md"
|
|
11
|
+
PROGRESS_FILE="$SCRIPT_DIR/.agent/logs/LOG.md"
|
|
12
|
+
HISTORY_DIR="$SCRIPT_DIR/.agent/history"
|
|
13
|
+
|
|
14
|
+
source "$SCRIPT_DIR/scripts/lib/constants.sh"
|
|
15
|
+
source "$SCRIPT_DIR/scripts/lib/logging.sh"
|
|
16
|
+
source "$SCRIPT_DIR/scripts/lib/preflight.sh"
|
|
17
|
+
source "$SCRIPT_DIR/scripts/lib/timing.sh"
|
|
18
|
+
source "$SCRIPT_DIR/scripts/lib/terminal.sh"
|
|
19
|
+
source "$SCRIPT_DIR/scripts/lib/spinner.sh"
|
|
20
|
+
source "$SCRIPT_DIR/scripts/lib/preview.sh"
|
|
21
|
+
source "$SCRIPT_DIR/scripts/lib/output.sh"
|
|
22
|
+
source "$SCRIPT_DIR/scripts/lib/cleanup.sh"
|
|
23
|
+
source "$SCRIPT_DIR/scripts/lib/promise.sh"
|
|
24
|
+
source "$SCRIPT_DIR/scripts/lib/notify.sh"
|
|
25
|
+
source "$SCRIPT_DIR/scripts/lib/display.sh"
|
|
26
|
+
source "$SCRIPT_DIR/scripts/lib/args.sh"
|
|
27
|
+
|
|
28
|
+
# Timing
|
|
29
|
+
START_TIME=$(date +%s)
|
|
30
|
+
ITERATION_TIMES=()
|
|
31
|
+
TOTAL_ITERATION_TIME=0
|
|
32
|
+
PREV_ITERATION_TIME=0
|
|
33
|
+
|
|
34
|
+
# Session ID for unique history file naming (YYYYMMDD-HHMMSS format). This is used to prevent overwrites between runs.
|
|
35
|
+
SESSION_ID=$(date +%Y%m%d-%H%M%S)
|
|
36
|
+
|
|
37
|
+
# Temporary files for spinner communication
|
|
38
|
+
STEP_FILE=$(mktemp)
|
|
39
|
+
PREVIEW_LINE_FILE=$(mktemp)
|
|
40
|
+
|
|
41
|
+
# Background process tracking for cleanup
|
|
42
|
+
AGENT_PID=""
|
|
43
|
+
OUTPUT_FILE=""
|
|
44
|
+
FULL_OUTPUT_FILE=""
|
|
45
|
+
|
|
46
|
+
# Set up traps
|
|
47
|
+
trap cleanup EXIT
|
|
48
|
+
trap handle_interrupt INT
|
|
49
|
+
|
|
50
|
+
# Parse arguments (sets MAX_ITERATIONS, ONCE_FLAG)
|
|
51
|
+
parse_arguments "$@"
|
|
52
|
+
|
|
53
|
+
# Initialize progress file if it doesn't exist
|
|
54
|
+
if [ ! -f "$PROGRESS_FILE" ]; then
|
|
55
|
+
echo "# Ralph Progress Log" > "$PROGRESS_FILE"
|
|
56
|
+
echo "Started: $(date)" >> "$PROGRESS_FILE"
|
|
57
|
+
echo "---" >> "$PROGRESS_FILE"
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
# Pre-flight checks
|
|
61
|
+
check_git_repo
|
|
62
|
+
check_required_files
|
|
63
|
+
check_history_dir
|
|
64
|
+
check_ansi_support
|
|
65
|
+
|
|
66
|
+
show_ralph
|
|
67
|
+
echo -e " ${C}Starting Ralph${R} ・ ${Y}v$VERSION${R} ・ Max iterations: ${Y}$MAX_ITERATIONS${R}"
|
|
68
|
+
echo ""
|
|
69
|
+
|
|
70
|
+
for i in $(seq 1 $MAX_ITERATIONS); do
|
|
71
|
+
ITERATION_START=$(date +%s)
|
|
72
|
+
|
|
73
|
+
# Initialize step timing for this iteration
|
|
74
|
+
init_iteration_step_times
|
|
75
|
+
|
|
76
|
+
echo -e "${B}░░▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░░${R}"
|
|
77
|
+
echo -e " ↪ ${R}Iteration ${Y}$i${R} of ${Y}$MAX_ITERATIONS${R}"
|
|
78
|
+
echo -e "${B}░░▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░░${R}"
|
|
79
|
+
echo -e ""
|
|
80
|
+
|
|
81
|
+
# Run Agent with the ralph prompt (prepend PROJECT_ROOT)
|
|
82
|
+
PROMPT_CONTENT="PROJECT_ROOT=$SCRIPT_DIR
|
|
83
|
+
|
|
84
|
+
$(cat $SCRIPT_DIR/.agent/PROMPT.md)"
|
|
85
|
+
|
|
86
|
+
# Start spinner
|
|
87
|
+
start_spinner
|
|
88
|
+
|
|
89
|
+
# Initialize rolling preview
|
|
90
|
+
init_rolling_preview
|
|
91
|
+
|
|
92
|
+
# Run Agent and capture output while updating spinner and preview
|
|
93
|
+
OUTPUT_FILE=$(mktemp)
|
|
94
|
+
FULL_OUTPUT_FILE=$(mktemp)
|
|
95
|
+
|
|
96
|
+
# Use script to provide pseudo-TTY for docker sandbox.
|
|
97
|
+
# This is the main command loop.
|
|
98
|
+
export PROMPT_CONTENT
|
|
99
|
+
export DOCKER_DEFAULT_PLATFORM=linux/amd64 # Needed for Playwright.
|
|
100
|
+
|
|
101
|
+
script -q "$OUTPUT_FILE" bash -c 'docker sandbox run --credentials host claude --model opus --output-format stream-json --verbose -p "$PROMPT_CONTENT"' >/dev/null 2>&1 &
|
|
102
|
+
AGENT_PID=$!
|
|
103
|
+
|
|
104
|
+
# Track position in output file for incremental reading
|
|
105
|
+
LAST_POS=0
|
|
106
|
+
|
|
107
|
+
# Monitor output and update spinner step and rolling preview
|
|
108
|
+
while kill -0 "$AGENT_PID" 2>/dev/null; do
|
|
109
|
+
if [ -f "$OUTPUT_FILE" ]; then
|
|
110
|
+
# Get current file size
|
|
111
|
+
CURRENT_SIZE=$(stat -f%z "$OUTPUT_FILE" 2>/dev/null || stat -c%s "$OUTPUT_FILE" 2>/dev/null || echo "0")
|
|
112
|
+
|
|
113
|
+
# Read new content if file has grown
|
|
114
|
+
if [ "$CURRENT_SIZE" -gt "$LAST_POS" ]; then
|
|
115
|
+
# Read new lines
|
|
116
|
+
tail -c +$((LAST_POS + 1)) "$OUTPUT_FILE" 2>/dev/null | while IFS= read -r line; do
|
|
117
|
+
if [ -n "$line" ]; then
|
|
118
|
+
# Parse JSON and extract text content
|
|
119
|
+
parsed=$(parse_json_content "$line")
|
|
120
|
+
if [ -n "$parsed" ]; then
|
|
121
|
+
# Save to full output
|
|
122
|
+
echo "$parsed" >> "$FULL_OUTPUT_FILE"
|
|
123
|
+
# Update spinner step
|
|
124
|
+
update_spinner_step "$parsed"
|
|
125
|
+
# Update preview line under spinner
|
|
126
|
+
update_preview_line "$parsed"
|
|
127
|
+
fi
|
|
128
|
+
fi
|
|
129
|
+
done
|
|
130
|
+
LAST_POS=$CURRENT_SIZE
|
|
131
|
+
fi
|
|
132
|
+
fi
|
|
133
|
+
sleep 0.2
|
|
134
|
+
done
|
|
135
|
+
|
|
136
|
+
wait "$AGENT_PID" || true
|
|
137
|
+
AGENT_PID="" # Clear PID after process exits
|
|
138
|
+
|
|
139
|
+
# Process any remaining output
|
|
140
|
+
if [ -f "$OUTPUT_FILE" ]; then
|
|
141
|
+
CURRENT_SIZE=$(stat -f%z "$OUTPUT_FILE" 2>/dev/null || stat -c%s "$OUTPUT_FILE" 2>/dev/null || echo "0")
|
|
142
|
+
if [ "$CURRENT_SIZE" -gt "$LAST_POS" ]; then
|
|
143
|
+
tail -c +$((LAST_POS + 1)) "$OUTPUT_FILE" 2>/dev/null | while IFS= read -r line; do
|
|
144
|
+
if [ -n "$line" ]; then
|
|
145
|
+
parsed=$(parse_json_content "$line")
|
|
146
|
+
if [ -n "$parsed" ]; then
|
|
147
|
+
echo "$parsed" >> "$FULL_OUTPUT_FILE"
|
|
148
|
+
fi
|
|
149
|
+
fi
|
|
150
|
+
done
|
|
151
|
+
fi
|
|
152
|
+
fi
|
|
153
|
+
|
|
154
|
+
OUTPUT=$(cat "$FULL_OUTPUT_FILE" 2>/dev/null || cat "$OUTPUT_FILE")
|
|
155
|
+
|
|
156
|
+
# Check for Docker daemon not ready error
|
|
157
|
+
if echo "$OUTPUT" | grep -q "docker daemon not ready"; then
|
|
158
|
+
stop_spinner
|
|
159
|
+
clear_rolling_preview
|
|
160
|
+
echo ""
|
|
161
|
+
echo -e "${RD}░░▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░░${R}"
|
|
162
|
+
echo -e " ❌ ${RD}Docker Error${R}"
|
|
163
|
+
echo -e " Docker daemon is not ready. Please ensure Docker is running."
|
|
164
|
+
echo -e "${RD}░░▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░░${R}"
|
|
165
|
+
rm -f "$OUTPUT_FILE" "$FULL_OUTPUT_FILE"
|
|
166
|
+
exit $EXIT_DOCKER_ERROR
|
|
167
|
+
fi
|
|
168
|
+
|
|
169
|
+
# Check for invalid API key / authentication error
|
|
170
|
+
if echo "$OUTPUT" | grep -q "Invalid API key"; then
|
|
171
|
+
stop_spinner
|
|
172
|
+
clear_rolling_preview
|
|
173
|
+
echo ""
|
|
174
|
+
echo -e "${RD}░░▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░░${R}"
|
|
175
|
+
echo -e " ❌ ${RD}Authentication Error${R}"
|
|
176
|
+
echo -e " Invalid API key. Please authenticate inside the Docker sandbox."
|
|
177
|
+
echo -e ""
|
|
178
|
+
echo -e " Run the following command and follow the login instructions:"
|
|
179
|
+
echo -e " ${C}docker sandbox run --credentials host claude${R}"
|
|
180
|
+
echo -e "${RD}░░▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░░${R}"
|
|
181
|
+
rm -f "$OUTPUT_FILE" "$FULL_OUTPUT_FILE"
|
|
182
|
+
exit $EXIT_AUTH_ERROR
|
|
183
|
+
fi
|
|
184
|
+
|
|
185
|
+
# Save cleaned output to history file (SESSION_ID prevents overwrites between runs)
|
|
186
|
+
# Strip ANSI control characters for clean, readable history
|
|
187
|
+
HISTORY_FILE="$HISTORY_DIR/ITERATION-${SESSION_ID}-${i}.txt"
|
|
188
|
+
strip_ansi_file "$OUTPUT_FILE" "$HISTORY_FILE"
|
|
189
|
+
|
|
190
|
+
# Extract final summary before removing files
|
|
191
|
+
FINAL_SUMMARY=$(extract_final_summary "$OUTPUT_FILE")
|
|
192
|
+
|
|
193
|
+
rm -f "$FULL_OUTPUT_FILE"
|
|
194
|
+
FULL_OUTPUT_FILE="" # Clear after removal
|
|
195
|
+
|
|
196
|
+
# Stop spinner
|
|
197
|
+
stop_spinner
|
|
198
|
+
|
|
199
|
+
# Finalize step timing for this iteration (record time spent in last step)
|
|
200
|
+
record_step_time ""
|
|
201
|
+
|
|
202
|
+
# Clear rolling preview area to make room for summary
|
|
203
|
+
clear_rolling_preview
|
|
204
|
+
|
|
205
|
+
# Display the final summary (persists after iteration)
|
|
206
|
+
if [ -n "$FINAL_SUMMARY" ]; then
|
|
207
|
+
display_final_summary "$FINAL_SUMMARY" 10
|
|
208
|
+
else
|
|
209
|
+
# Fallback: show last 10 lines of parsed output if no result found
|
|
210
|
+
FALLBACK_SUMMARY=$(echo "$OUTPUT" | tail -n 10)
|
|
211
|
+
if [ -n "$FALLBACK_SUMMARY" ]; then
|
|
212
|
+
display_final_summary "$FALLBACK_SUMMARY" 10
|
|
213
|
+
fi
|
|
214
|
+
fi
|
|
215
|
+
|
|
216
|
+
# Clean up the raw output file (history file already saved)
|
|
217
|
+
rm -f "$OUTPUT_FILE"
|
|
218
|
+
OUTPUT_FILE="" # Clear after removal
|
|
219
|
+
|
|
220
|
+
# Calculate iteration duration
|
|
221
|
+
ITERATION_END=$(date +%s)
|
|
222
|
+
ITERATION_DURATION=$((ITERATION_END - ITERATION_START))
|
|
223
|
+
ITERATION_TIMES+=($ITERATION_DURATION)
|
|
224
|
+
TOTAL_ITERATION_TIME=$((TOTAL_ITERATION_TIME + ITERATION_DURATION))
|
|
225
|
+
ITERATION_AVG=$((TOTAL_ITERATION_TIME / ${#ITERATION_TIMES[@]}))
|
|
226
|
+
ITERATION_STR=$(format_duration $ITERATION_DURATION)
|
|
227
|
+
AVG_STR=$(format_duration $ITERATION_AVG)
|
|
228
|
+
DELTA_STR=$(format_delta $ITERATION_DURATION $PREV_ITERATION_TIME)
|
|
229
|
+
PREV_ITERATION_TIME=$ITERATION_DURATION
|
|
230
|
+
|
|
231
|
+
# Check for completion signal
|
|
232
|
+
# Note: We check both $OUTPUT and $FINAL_SUMMARY because the JSON parsing
|
|
233
|
+
# in parse_json_content truncates text at escaped quotes, but FINAL_SUMMARY
|
|
234
|
+
# is correctly extracted using jq from the result message
|
|
235
|
+
if has_complete_tag "$OUTPUT" || has_complete_tag "$FINAL_SUMMARY"; then
|
|
236
|
+
ELAPSED=$(($(date +%s) - START_TIME))
|
|
237
|
+
ELAPSED_STR=$(format_duration $ELAPSED)
|
|
238
|
+
echo ""
|
|
239
|
+
echo -e "${GR}░░▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░░${R}"
|
|
240
|
+
echo -e " 🎉 ${GR}Ralph completed all tasks!${R}"
|
|
241
|
+
echo -e " ✅ Finished at iteration ${GR}$i${R} of ${GR}$MAX_ITERATIONS${R}"
|
|
242
|
+
if [ -n "$DELTA_STR" ]; then
|
|
243
|
+
echo -e " ⏱️ Iteration $i: ${Y}$ITERATION_STR${R} ($DELTA_STR) ${C}│${R} Average: ${Y}$AVG_STR${R}"
|
|
244
|
+
else
|
|
245
|
+
echo -e " ⏱️ Iteration $i: ${Y}$ITERATION_STR${R} ${C}│${R} Average: ${Y}$AVG_STR${R}"
|
|
246
|
+
fi
|
|
247
|
+
echo -e " ⏱️ Total time: ${Y}$ELAPSED_STR${R}"
|
|
248
|
+
display_session_step_totals
|
|
249
|
+
echo -e "${GR}░░▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░░${R}"
|
|
250
|
+
exit $EXIT_COMPLETE
|
|
251
|
+
fi
|
|
252
|
+
|
|
253
|
+
# Check for BLOCKED tag - agent needs human help
|
|
254
|
+
# Check both $OUTPUT and $FINAL_SUMMARY (see completion signal comment above)
|
|
255
|
+
if has_blocked_tag "$OUTPUT" || has_blocked_tag "$FINAL_SUMMARY"; then
|
|
256
|
+
BLOCKED_REASON=$(extract_blocked_reason "$OUTPUT")
|
|
257
|
+
# If not found in OUTPUT, try FINAL_SUMMARY
|
|
258
|
+
[ -z "$BLOCKED_REASON" ] && BLOCKED_REASON=$(extract_blocked_reason "$FINAL_SUMMARY")
|
|
259
|
+
ELAPSED=$(($(date +%s) - START_TIME))
|
|
260
|
+
ELAPSED_STR=$(format_duration $ELAPSED)
|
|
261
|
+
play_notification_sound
|
|
262
|
+
show_notification "Ralph - BLOCKED" "$BLOCKED_REASON"
|
|
263
|
+
display_blocked_message "$BLOCKED_REASON" "$i"
|
|
264
|
+
if [ -n "$DELTA_STR" ]; then
|
|
265
|
+
echo -e " ⏱️ Iteration $i: ${Y}$ITERATION_STR${R} ($DELTA_STR) ${C}│${R} Average: ${Y}$AVG_STR${R}"
|
|
266
|
+
else
|
|
267
|
+
echo -e " ⏱️ Iteration $i: ${Y}$ITERATION_STR${R} ${C}│${R} Average: ${Y}$AVG_STR${R}"
|
|
268
|
+
fi
|
|
269
|
+
echo -e " ⏱️ Total time: ${Y}$ELAPSED_STR${R}"
|
|
270
|
+
display_session_step_totals
|
|
271
|
+
exit $EXIT_BLOCKED
|
|
272
|
+
fi
|
|
273
|
+
|
|
274
|
+
# Check for DECIDE tag - agent needs human decision
|
|
275
|
+
# Check both $OUTPUT and $FINAL_SUMMARY (see completion signal comment above)
|
|
276
|
+
if has_decide_tag "$OUTPUT" || has_decide_tag "$FINAL_SUMMARY"; then
|
|
277
|
+
DECIDE_QUESTION=$(extract_decide_question "$OUTPUT")
|
|
278
|
+
# If not found in OUTPUT, try FINAL_SUMMARY
|
|
279
|
+
[ -z "$DECIDE_QUESTION" ] && DECIDE_QUESTION=$(extract_decide_question "$FINAL_SUMMARY")
|
|
280
|
+
ELAPSED=$(($(date +%s) - START_TIME))
|
|
281
|
+
ELAPSED_STR=$(format_duration $ELAPSED)
|
|
282
|
+
play_notification_sound
|
|
283
|
+
show_notification "Ralph - Decision Needed" "$DECIDE_QUESTION"
|
|
284
|
+
display_decide_message "$DECIDE_QUESTION" "$i"
|
|
285
|
+
if [ -n "$DELTA_STR" ]; then
|
|
286
|
+
echo -e " ⏱️ Iteration $i: ${Y}$ITERATION_STR${R} ($DELTA_STR) ${C}│${R} Average: ${Y}$AVG_STR${R}"
|
|
287
|
+
else
|
|
288
|
+
echo -e " ⏱️ Iteration $i: ${Y}$ITERATION_STR${R} ${C}│${R} Average: ${Y}$AVG_STR${R}"
|
|
289
|
+
fi
|
|
290
|
+
echo -e " ⏱️ Total time: ${Y}$ELAPSED_STR${R}"
|
|
291
|
+
display_session_step_totals
|
|
292
|
+
exit $EXIT_DECIDE
|
|
293
|
+
fi
|
|
294
|
+
|
|
295
|
+
# Calculate elapsed time
|
|
296
|
+
ELAPSED=$(($(date +%s) - START_TIME))
|
|
297
|
+
ELAPSED_STR=$(format_duration $ELAPSED)
|
|
298
|
+
|
|
299
|
+
if [ -n "$DELTA_STR" ]; then
|
|
300
|
+
echo -e "${G} └── ✓ Iteration $i complete${R} ${C}│${R} Iteration: ${Y}$ITERATION_STR${R} ($DELTA_STR) ${C}│${R} Average: ${Y}$AVG_STR${R} ${C}│${R} Total: ${Y}$ELAPSED_STR${R}"
|
|
301
|
+
else
|
|
302
|
+
echo -e "${G} └── ✓ Iteration $i complete${R} ${C}│${R} Iteration: ${Y}$ITERATION_STR${R} ${C}│${R} Average: ${Y}$AVG_STR${R} ${C}│${R} Total: ${Y}$ELAPSED_STR${R}"
|
|
303
|
+
fi
|
|
304
|
+
|
|
305
|
+
# Display per-iteration step times
|
|
306
|
+
STEP_TIMES_OUTPUT=$(format_step_times "ITERATION")
|
|
307
|
+
if [ -n "$STEP_TIMES_OUTPUT" ]; then
|
|
308
|
+
echo -e "${G} └──${R} $STEP_TIMES_OUTPUT"
|
|
309
|
+
fi
|
|
310
|
+
sleep 2
|
|
311
|
+
done
|
|
312
|
+
|
|
313
|
+
# Calculate final elapsed time
|
|
314
|
+
ELAPSED=$(($(date +%s) - START_TIME))
|
|
315
|
+
ELAPSED_STR=$(format_duration $ELAPSED)
|
|
316
|
+
|
|
317
|
+
# Calculate final average (if any iterations completed)
|
|
318
|
+
if [ ${#ITERATION_TIMES[@]} -gt 0 ]; then
|
|
319
|
+
FINAL_AVG=$((TOTAL_ITERATION_TIME / ${#ITERATION_TIMES[@]}))
|
|
320
|
+
FINAL_AVG_STR=$(format_duration $FINAL_AVG)
|
|
321
|
+
fi
|
|
322
|
+
|
|
323
|
+
echo ""
|
|
324
|
+
echo -e "${Y}░░▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░░${R}"
|
|
325
|
+
echo -e " ⚠️ ${Y}Ralph reached max iterations${R} (${M}$MAX_ITERATIONS${R})"
|
|
326
|
+
if [ ${#ITERATION_TIMES[@]} -gt 0 ]; then
|
|
327
|
+
echo -e " ⏱️ Average iteration time: ${Y}$FINAL_AVG_STR${R}"
|
|
328
|
+
fi
|
|
329
|
+
echo -e " ⏱️ Total time: ${Y}$ELAPSED_STR${R}"
|
|
330
|
+
display_session_step_totals
|
|
331
|
+
echo -e " 📋 Check progress: ${G}$PROGRESS_FILE${R}"
|
|
332
|
+
echo -e "${Y}░░▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░░${R}"
|
|
333
|
+
exit $EXIT_MAX_ITERATIONS
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Arguments module for ralph.sh
|
|
3
|
+
# CLI argument parsing
|
|
4
|
+
# Dependencies: constants.sh
|
|
5
|
+
|
|
6
|
+
# Parse command line arguments
|
|
7
|
+
# Sets: MAX_ITERATIONS, ONCE_FLAG
|
|
8
|
+
# Usage: parse_arguments "$@"
|
|
9
|
+
parse_arguments() {
|
|
10
|
+
MAX_ITERATIONS=10
|
|
11
|
+
ONCE_FLAG=false
|
|
12
|
+
|
|
13
|
+
while [[ $# -gt 0 ]]; do
|
|
14
|
+
case $1 in
|
|
15
|
+
--help|-h)
|
|
16
|
+
show_help
|
|
17
|
+
;;
|
|
18
|
+
--once)
|
|
19
|
+
ONCE_FLAG=true
|
|
20
|
+
shift
|
|
21
|
+
;;
|
|
22
|
+
--max-iterations|-n)
|
|
23
|
+
MAX_ITERATIONS="$2"
|
|
24
|
+
shift 2
|
|
25
|
+
;;
|
|
26
|
+
--max-iterations=*)
|
|
27
|
+
MAX_ITERATIONS="${1#*=}"
|
|
28
|
+
shift
|
|
29
|
+
;;
|
|
30
|
+
[0-9]*)
|
|
31
|
+
MAX_ITERATIONS="$1"
|
|
32
|
+
shift
|
|
33
|
+
;;
|
|
34
|
+
*)
|
|
35
|
+
shift
|
|
36
|
+
;;
|
|
37
|
+
esac
|
|
38
|
+
done
|
|
39
|
+
|
|
40
|
+
# --once overrides max-iterations
|
|
41
|
+
if [ "$ONCE_FLAG" = true ]; then
|
|
42
|
+
MAX_ITERATIONS=1
|
|
43
|
+
fi
|
|
44
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Cleanup module for ralph.sh
|
|
3
|
+
# Signal handlers and process cleanup
|
|
4
|
+
# Dependencies: constants.sh
|
|
5
|
+
#
|
|
6
|
+
# Properly cleans up all background processes and temporary files on exit.
|
|
7
|
+
# Runs on both normal exit (EXIT trap) and interrupt (called from handle_interrupt).
|
|
8
|
+
# Ensures no orphan processes remain after script exits.
|
|
9
|
+
|
|
10
|
+
# Cleanup function - kills background processes and removes temp files
|
|
11
|
+
cleanup() {
|
|
12
|
+
# Kill spinner background process
|
|
13
|
+
if [ -n "$SPINNER_PID" ] && kill -0 "$SPINNER_PID" 2>/dev/null; then
|
|
14
|
+
kill "$SPINNER_PID" 2>/dev/null || true
|
|
15
|
+
wait "$SPINNER_PID" 2>/dev/null || true
|
|
16
|
+
fi
|
|
17
|
+
SPINNER_PID=""
|
|
18
|
+
|
|
19
|
+
# Kill Agent process if still running
|
|
20
|
+
if [ -n "$AGENT_PID" ] && kill -0 "$AGENT_PID" 2>/dev/null; then
|
|
21
|
+
kill "$AGENT_PID" 2>/dev/null || true
|
|
22
|
+
wait "$AGENT_PID" 2>/dev/null || true
|
|
23
|
+
fi
|
|
24
|
+
AGENT_PID=""
|
|
25
|
+
|
|
26
|
+
# Remove temporary files
|
|
27
|
+
[[ -n "$STEP_FILE" ]] && rm -f "$STEP_FILE" 2>/dev/null || true
|
|
28
|
+
[[ -n "$PREVIEW_LINE_FILE" ]] && rm -f "$PREVIEW_LINE_FILE" 2>/dev/null || true
|
|
29
|
+
[[ -n "$OUTPUT_FILE" ]] && rm -f "$OUTPUT_FILE" 2>/dev/null || true
|
|
30
|
+
[[ -n "$FULL_OUTPUT_FILE" ]] && rm -f "$FULL_OUTPUT_FILE" 2>/dev/null || true
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
# Double CTRL+C to exit
|
|
34
|
+
# Tracks last interrupt time, requires second press within 3 seconds to exit
|
|
35
|
+
LAST_INTERRUPT=0
|
|
36
|
+
|
|
37
|
+
handle_interrupt() {
|
|
38
|
+
local current_time=$(date +%s)
|
|
39
|
+
local time_diff=$((current_time - LAST_INTERRUPT))
|
|
40
|
+
|
|
41
|
+
if [ $time_diff -lt 3 ]; then
|
|
42
|
+
# Second CTRL+C within 3 seconds - exit
|
|
43
|
+
echo ""
|
|
44
|
+
echo -e "${Y}Exiting...${R}"
|
|
45
|
+
cleanup
|
|
46
|
+
exit 130
|
|
47
|
+
else
|
|
48
|
+
# First CTRL+C - show warning and update timestamp
|
|
49
|
+
LAST_INTERRUPT=$current_time
|
|
50
|
+
echo ""
|
|
51
|
+
echo -e "${Y}⚠️ Press CTRL+C again within 3 seconds to exit${R}"
|
|
52
|
+
fi
|
|
53
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Constants module for ralph.sh
|
|
3
|
+
# Contains: Colors, paths, version, exit codes
|
|
4
|
+
|
|
5
|
+
# Version
|
|
6
|
+
VERSION="1.0.0"
|
|
7
|
+
|
|
8
|
+
# Colors (short syntax)
|
|
9
|
+
Y='\033[33m' # Yellow
|
|
10
|
+
B='\033[94m' # Light blue
|
|
11
|
+
G='\033[1;90m' # Grey bold
|
|
12
|
+
C='\033[36m' # Cyan
|
|
13
|
+
M='\033[35m' # Magenta
|
|
14
|
+
GR='\033[32m' # Green
|
|
15
|
+
RD='\033[31m' # Red
|
|
16
|
+
D='\033[2m' # Dim
|
|
17
|
+
R='\033[0m' # Reset
|
|
18
|
+
|
|
19
|
+
# Exit codes
|
|
20
|
+
EXIT_COMPLETE=0
|
|
21
|
+
EXIT_MAX_ITERATIONS=1
|
|
22
|
+
EXIT_BLOCKED=2
|
|
23
|
+
EXIT_DECIDE=3
|
|
24
|
+
EXIT_DOCKER_ERROR=4
|
|
25
|
+
EXIT_AUTH_ERROR=5
|