agentic-loop 3.1.5 → 3.2.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/package.json +1 -1
- package/ralph/loop.sh +6 -27
- package/ralph/setup.sh +76 -11
- package/ralph/verify.sh +16 -284
- package/templates/PROMPT.md +26 -43
- package/ralph/api.sh +0 -216
- package/ralph/browser-verify/README.md +0 -135
- package/ralph/browser-verify/verify.ts +0 -450
- package/ralph/playwright.sh +0 -238
- package/ralph/verify/browser.sh +0 -324
- package/ralph/verify/review.sh +0 -152
package/ralph/api.sh
DELETED
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# shellcheck shell=bash
|
|
3
|
-
# api.sh - API validation for backend stories
|
|
4
|
-
|
|
5
|
-
# Parse an endpoint string into method and path
|
|
6
|
-
# Usage: parse_endpoint "POST /api/users" "http://localhost:3000"
|
|
7
|
-
# Sets: ENDPOINT_METHOD, ENDPOINT_PATH, ENDPOINT_URL
|
|
8
|
-
parse_endpoint() {
|
|
9
|
-
local endpoint="$1"
|
|
10
|
-
local base_url="${2:-}"
|
|
11
|
-
|
|
12
|
-
# Defaults
|
|
13
|
-
ENDPOINT_METHOD="GET"
|
|
14
|
-
ENDPOINT_PATH="$endpoint"
|
|
15
|
-
ENDPOINT_URL=""
|
|
16
|
-
|
|
17
|
-
# Parse method if present (e.g., "POST /api/contact")
|
|
18
|
-
if [[ "$endpoint" =~ ^(GET|POST|PUT|PATCH|DELETE)[[:space:]]+(.*) ]]; then
|
|
19
|
-
ENDPOINT_METHOD="${BASH_REMATCH[1]}"
|
|
20
|
-
ENDPOINT_PATH="${BASH_REMATCH[2]}"
|
|
21
|
-
fi
|
|
22
|
-
|
|
23
|
-
# Build full URL
|
|
24
|
-
if [[ "$ENDPOINT_PATH" =~ ^https?:// ]]; then
|
|
25
|
-
ENDPOINT_URL="$ENDPOINT_PATH"
|
|
26
|
-
elif [[ -n "$base_url" ]]; then
|
|
27
|
-
ENDPOINT_URL="${base_url}${ENDPOINT_PATH}"
|
|
28
|
-
fi
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
# Check if endpoint is a WebSocket (can't be tested with HTTP)
|
|
32
|
-
is_websocket_endpoint() {
|
|
33
|
-
local endpoint="$1"
|
|
34
|
-
[[ "$endpoint" =~ ^wss?:// ]] || [[ "$endpoint" =~ ^(GET|POST|PUT|PATCH|DELETE)[[:space:]]+wss?:// ]]
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
# Validate API endpoints for a backend story
|
|
38
|
-
run_api_validation() {
|
|
39
|
-
local story="$1"
|
|
40
|
-
|
|
41
|
-
# Get API endpoints from story
|
|
42
|
-
local endpoints
|
|
43
|
-
endpoints=$(jq -r --arg id "$story" '.stories[] | select(.id==$id) | .apiEndpoints[]?' "$RALPH_DIR/prd.json" 2>/dev/null)
|
|
44
|
-
|
|
45
|
-
if [[ -z "$endpoints" ]]; then
|
|
46
|
-
echo " (no apiEndpoints defined, skipping API validation)"
|
|
47
|
-
return 0
|
|
48
|
-
fi
|
|
49
|
-
|
|
50
|
-
# Get base URL from config or use default
|
|
51
|
-
local base_url
|
|
52
|
-
base_url=$(get_config '.api.baseUrl' "http://localhost:3000")
|
|
53
|
-
|
|
54
|
-
local failed=0
|
|
55
|
-
|
|
56
|
-
echo " Validating API endpoints..."
|
|
57
|
-
|
|
58
|
-
while IFS= read -r endpoint; do
|
|
59
|
-
[[ -z "$endpoint" ]] && continue
|
|
60
|
-
|
|
61
|
-
# Skip WebSocket endpoints - they can't be tested with HTTP curl
|
|
62
|
-
if is_websocket_endpoint "$endpoint"; then
|
|
63
|
-
echo " Skipping WebSocket endpoint: $endpoint (use integration tests)"
|
|
64
|
-
continue
|
|
65
|
-
fi
|
|
66
|
-
|
|
67
|
-
# Parse endpoint into method, path, and full URL
|
|
68
|
-
parse_endpoint "$endpoint" "$base_url"
|
|
69
|
-
|
|
70
|
-
echo -n " $ENDPOINT_METHOD $ENDPOINT_PATH... "
|
|
71
|
-
|
|
72
|
-
# Make the request
|
|
73
|
-
local response_code
|
|
74
|
-
response_code=$(curl -sf -m "$CURL_TIMEOUT_SECONDS" -o /dev/null -w "%{http_code}" -X "$ENDPOINT_METHOD" "$ENDPOINT_URL" 2>/dev/null)
|
|
75
|
-
|
|
76
|
-
if [[ "$response_code" =~ ^2[0-9][0-9]$ ]]; then
|
|
77
|
-
print_success "$response_code"
|
|
78
|
-
elif [[ "$response_code" == "000" ]]; then
|
|
79
|
-
print_error "connection failed"
|
|
80
|
-
failed=1
|
|
81
|
-
elif [[ "$response_code" == "422" || "$response_code" == "400" ]]; then
|
|
82
|
-
# 422/400 = endpoint exists but needs params - don't fail verification
|
|
83
|
-
print_warning "$response_code (needs params)"
|
|
84
|
-
elif [[ "$response_code" == "401" || "$response_code" == "403" ]]; then
|
|
85
|
-
# Auth required - endpoint exists
|
|
86
|
-
print_warning "$response_code (auth required)"
|
|
87
|
-
else
|
|
88
|
-
print_error "$response_code"
|
|
89
|
-
failed=1
|
|
90
|
-
fi
|
|
91
|
-
done <<< "$endpoints"
|
|
92
|
-
|
|
93
|
-
return $failed
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
# Run comprehensive API tests for a story
|
|
97
|
-
run_api_tests() {
|
|
98
|
-
local story="$1"
|
|
99
|
-
|
|
100
|
-
# Get test steps that look like API calls
|
|
101
|
-
local test_steps
|
|
102
|
-
test_steps=$(jq -r --arg id "$story" '.stories[] | select(.id==$id) | .testSteps[]?' "$RALPH_DIR/prd.json" 2>/dev/null)
|
|
103
|
-
|
|
104
|
-
if [[ -z "$test_steps" ]]; then
|
|
105
|
-
return 0
|
|
106
|
-
fi
|
|
107
|
-
|
|
108
|
-
local failed=0
|
|
109
|
-
local log_file
|
|
110
|
-
log_file=$(create_temp_file ".log") || return 1
|
|
111
|
-
|
|
112
|
-
echo " Running API test steps..."
|
|
113
|
-
|
|
114
|
-
while IFS= read -r step; do
|
|
115
|
-
[[ -z "$step" ]] && continue
|
|
116
|
-
|
|
117
|
-
# Check if this looks like a curl command or API test
|
|
118
|
-
if [[ "$step" =~ ^curl ]]; then
|
|
119
|
-
echo -n " $step... "
|
|
120
|
-
|
|
121
|
-
if safe_exec "$step" "$log_file"; then
|
|
122
|
-
print_success "passed"
|
|
123
|
-
else
|
|
124
|
-
print_error "failed"
|
|
125
|
-
echo ""
|
|
126
|
-
echo " Response:"
|
|
127
|
-
tail -"$MAX_OUTPUT_PREVIEW_LINES" "$log_file" | sed 's/^/ /'
|
|
128
|
-
failed=1
|
|
129
|
-
fi
|
|
130
|
-
fi
|
|
131
|
-
done <<< "$test_steps"
|
|
132
|
-
|
|
133
|
-
rm -f "$log_file"
|
|
134
|
-
return $failed
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
# Validate error handling for API
|
|
138
|
-
run_api_error_tests() {
|
|
139
|
-
local story="$1"
|
|
140
|
-
|
|
141
|
-
# Get error handling requirements
|
|
142
|
-
local error_handling
|
|
143
|
-
error_handling=$(jq -r --arg id "$story" '.stories[] | select(.id==$id) | .errorHandling[]?' "$RALPH_DIR/prd.json" 2>/dev/null)
|
|
144
|
-
|
|
145
|
-
if [[ -z "$error_handling" ]]; then
|
|
146
|
-
return 0
|
|
147
|
-
fi
|
|
148
|
-
|
|
149
|
-
# Get base URL and endpoints
|
|
150
|
-
local base_url
|
|
151
|
-
base_url=$(get_config '.api.baseUrl' "http://localhost:3000")
|
|
152
|
-
|
|
153
|
-
local endpoints
|
|
154
|
-
endpoints=$(jq -r --arg id "$story" '.stories[] | select(.id==$id) | .apiEndpoints[0]?' "$RALPH_DIR/prd.json" 2>/dev/null)
|
|
155
|
-
|
|
156
|
-
if [[ -z "$endpoints" ]]; then
|
|
157
|
-
return 0
|
|
158
|
-
fi
|
|
159
|
-
|
|
160
|
-
# Skip WebSocket endpoints
|
|
161
|
-
if is_websocket_endpoint "$endpoints"; then
|
|
162
|
-
echo " Skipping error tests for WebSocket endpoint"
|
|
163
|
-
return 0
|
|
164
|
-
fi
|
|
165
|
-
|
|
166
|
-
# Parse endpoint (default to POST for error tests)
|
|
167
|
-
parse_endpoint "$endpoints" "$base_url"
|
|
168
|
-
[[ "$ENDPOINT_METHOD" == "GET" ]] && ENDPOINT_METHOD="POST"
|
|
169
|
-
|
|
170
|
-
local failed=0
|
|
171
|
-
|
|
172
|
-
echo " Testing API error handling..."
|
|
173
|
-
|
|
174
|
-
# Test common error cases
|
|
175
|
-
while IFS= read -r error_case; do
|
|
176
|
-
[[ -z "$error_case" ]] && continue
|
|
177
|
-
|
|
178
|
-
# Check for 400 tests (bad input)
|
|
179
|
-
if [[ "$error_case" =~ 400 ]]; then
|
|
180
|
-
echo -n " Testing 400 (bad request)... "
|
|
181
|
-
|
|
182
|
-
local response_code
|
|
183
|
-
response_code=$(curl -sf -m "$CURL_TIMEOUT_SECONDS" -o /dev/null -w "%{http_code}" \
|
|
184
|
-
-X "$ENDPOINT_METHOD" \
|
|
185
|
-
-H "Content-Type: application/json" \
|
|
186
|
-
-d '{}' \
|
|
187
|
-
"$ENDPOINT_URL" 2>/dev/null)
|
|
188
|
-
|
|
189
|
-
if [[ "$response_code" == "400" ]]; then
|
|
190
|
-
print_success "correctly returns 400"
|
|
191
|
-
else
|
|
192
|
-
print_warning "got $response_code (expected 400)"
|
|
193
|
-
failed=1
|
|
194
|
-
fi
|
|
195
|
-
fi
|
|
196
|
-
|
|
197
|
-
# Check for 401 tests (unauthorized)
|
|
198
|
-
if [[ "$error_case" =~ 401 ]]; then
|
|
199
|
-
echo -n " Testing 401 (unauthorized)... "
|
|
200
|
-
|
|
201
|
-
local response_code
|
|
202
|
-
response_code=$(curl -sf -m "$CURL_TIMEOUT_SECONDS" -o /dev/null -w "%{http_code}" \
|
|
203
|
-
-X "$ENDPOINT_METHOD" \
|
|
204
|
-
"$ENDPOINT_URL" 2>/dev/null)
|
|
205
|
-
|
|
206
|
-
if [[ "$response_code" == "401" ]]; then
|
|
207
|
-
print_success "correctly returns 401"
|
|
208
|
-
else
|
|
209
|
-
print_warning "got $response_code (expected 401)"
|
|
210
|
-
failed=1
|
|
211
|
-
fi
|
|
212
|
-
fi
|
|
213
|
-
done <<< "$error_handling"
|
|
214
|
-
|
|
215
|
-
return $failed
|
|
216
|
-
}
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
# Browser Verify Skill
|
|
2
|
-
|
|
3
|
-
Verify that a web page loads correctly using real browser automation (Playwright).
|
|
4
|
-
|
|
5
|
-
## Purpose
|
|
6
|
-
|
|
7
|
-
This skill launches a real Chromium browser to verify pages work correctly. Unlike asking Claude to "imagine" what a page looks like, this actually:
|
|
8
|
-
|
|
9
|
-
- Loads the page in a real browser
|
|
10
|
-
- Detects JavaScript console errors
|
|
11
|
-
- Detects failed network requests
|
|
12
|
-
- Checks for error messages on the page ("Oops!", "Something went wrong")
|
|
13
|
-
- Verifies required elements exist
|
|
14
|
-
- Takes screenshots for evidence
|
|
15
|
-
- Tests mobile viewport
|
|
16
|
-
|
|
17
|
-
## When to Use
|
|
18
|
-
|
|
19
|
-
Use this skill for **frontend story verification**:
|
|
20
|
-
- After implementing a UI feature
|
|
21
|
-
- To verify a page loads without errors
|
|
22
|
-
- To check that required elements render
|
|
23
|
-
- To test mobile responsiveness
|
|
24
|
-
|
|
25
|
-
## Usage
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
npx tsx ralph/browser-verify/verify.ts <url> [options]
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
### Options
|
|
32
|
-
|
|
33
|
-
| Option | Description | Default |
|
|
34
|
-
|--------|-------------|---------|
|
|
35
|
-
| `--selectors '["#app", ".btn"]'` | Required elements (JSON array) | `[]` |
|
|
36
|
-
| `--screenshot <path>` | Save screenshot to path | none |
|
|
37
|
-
| `--timeout <ms>` | Page load timeout | 30000 |
|
|
38
|
-
| `--headless` | Run without visible browser | true |
|
|
39
|
-
| `--no-headless` | Show browser window | false |
|
|
40
|
-
| `--check-console` | Fail on console errors | true |
|
|
41
|
-
| `--no-check-console` | Ignore console errors | false |
|
|
42
|
-
| `--mobile` | Use mobile viewport (375x667) | false |
|
|
43
|
-
| `--viewport <W>x<H>` | Custom viewport size | 1280x720 |
|
|
44
|
-
|
|
45
|
-
### Examples
|
|
46
|
-
|
|
47
|
-
**Basic page check:**
|
|
48
|
-
```bash
|
|
49
|
-
npx tsx ralph/browser-verify/verify.ts http://localhost:3000/dashboard
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
**Check specific elements exist:**
|
|
53
|
-
```bash
|
|
54
|
-
npx tsx ralph/browser-verify/verify.ts http://localhost:3000/login \
|
|
55
|
-
--selectors '["#email", "#password", "button[type=submit]"]'
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
**Take screenshot and check mobile:**
|
|
59
|
-
```bash
|
|
60
|
-
npx tsx ralph/browser-verify/verify.ts http://localhost:3000/dashboard \
|
|
61
|
-
--screenshot .ralph/screenshots/dashboard.png \
|
|
62
|
-
--mobile
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
**Debug mode (visible browser):**
|
|
66
|
-
```bash
|
|
67
|
-
npx tsx ralph/browser-verify/verify.ts http://localhost:3000/dashboard \
|
|
68
|
-
--no-headless
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
## Output
|
|
72
|
-
|
|
73
|
-
Returns JSON to stdout:
|
|
74
|
-
|
|
75
|
-
```json
|
|
76
|
-
{
|
|
77
|
-
"pass": true,
|
|
78
|
-
"errors": [],
|
|
79
|
-
"warnings": ["Page loaded slowly (3500ms)"],
|
|
80
|
-
"screenshot": ".ralph/screenshots/dashboard.png",
|
|
81
|
-
"title": "Dashboard - MyApp",
|
|
82
|
-
"elementsFound": ["#app", ".header", ".sidebar"],
|
|
83
|
-
"elementsMissing": [],
|
|
84
|
-
"consoleErrors": [],
|
|
85
|
-
"networkErrors": [],
|
|
86
|
-
"loadTime": 1234
|
|
87
|
-
}
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
### Exit Codes
|
|
91
|
-
|
|
92
|
-
- `0` - Verification passed
|
|
93
|
-
- `1` - Verification failed (check `errors` array)
|
|
94
|
-
|
|
95
|
-
## What It Catches
|
|
96
|
-
|
|
97
|
-
| Issue | How It's Detected |
|
|
98
|
-
|-------|-------------------|
|
|
99
|
-
| Page won't load | Connection refused, timeout |
|
|
100
|
-
| JavaScript errors | Console error messages |
|
|
101
|
-
| Failed API calls | Network request failures, 4xx/5xx responses |
|
|
102
|
-
| Error pages | Text matching "Oops!", "Something went wrong", etc. |
|
|
103
|
-
| Missing elements | Selector not found in DOM |
|
|
104
|
-
| Slow pages | Load time > 5 seconds (warning) |
|
|
105
|
-
|
|
106
|
-
## Integration with Ralph
|
|
107
|
-
|
|
108
|
-
Ralph automatically uses this skill for frontend story verification:
|
|
109
|
-
|
|
110
|
-
1. Story has `testUrl` defined
|
|
111
|
-
2. Ralph calls `verify.ts` with the URL
|
|
112
|
-
3. Optionally checks `selectors` from story config
|
|
113
|
-
4. Takes screenshot for evidence
|
|
114
|
-
5. Fails verification if errors detected
|
|
115
|
-
|
|
116
|
-
## Requirements
|
|
117
|
-
|
|
118
|
-
- Node.js 18+
|
|
119
|
-
- Playwright (`npx playwright install chromium`)
|
|
120
|
-
|
|
121
|
-
## Troubleshooting
|
|
122
|
-
|
|
123
|
-
**"Cannot find module 'playwright'"**
|
|
124
|
-
```bash
|
|
125
|
-
npm install playwright
|
|
126
|
-
npx playwright install chromium
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
**"Browser closed unexpectedly"**
|
|
130
|
-
- Try with `--no-headless` to see what's happening
|
|
131
|
-
- Check if the URL is accessible
|
|
132
|
-
|
|
133
|
-
**"Timeout waiting for page"**
|
|
134
|
-
- Increase timeout: `--timeout 60000`
|
|
135
|
-
- Check if dev server is running
|