vibe-and-thrive 1.6.8 → 1.7.1
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/init.sh +7 -0
- package/ralph/loop.sh +147 -96
- package/ralph/utils.sh +40 -2
- package/ralph/verify/browser.sh +310 -0
- package/ralph/verify/lint.sh +281 -0
- package/ralph/verify/review.sh +155 -0
- package/ralph/verify/tests.sh +80 -0
- package/ralph/verify.sh +16 -813
package/package.json
CHANGED
package/ralph/init.sh
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
# init.sh - Initialize ralph in a project
|
|
3
3
|
|
|
4
4
|
ralph_init() {
|
|
5
|
+
# Check if already initialized (progress.txt is created by full init)
|
|
6
|
+
if [[ -f "$RALPH_DIR/progress.txt" ]]; then
|
|
7
|
+
echo "Ralph already initialized in this directory."
|
|
8
|
+
echo "Use 'ralph run' to start the loop or 'ralph status' to check status."
|
|
9
|
+
return 0
|
|
10
|
+
fi
|
|
11
|
+
|
|
5
12
|
echo "Initializing ralph..."
|
|
6
13
|
|
|
7
14
|
# Create directory structure
|
package/ralph/loop.sh
CHANGED
|
@@ -251,55 +251,59 @@ startup_checklist() {
|
|
|
251
251
|
fi
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
-
#
|
|
255
|
-
|
|
256
|
-
local
|
|
257
|
-
local failure_context="${2:-}"
|
|
258
|
-
|
|
259
|
-
# Read base PROMPT.md
|
|
260
|
-
cat "$PROMPT_FILE"
|
|
254
|
+
# Helper: Inject story details into prompt
|
|
255
|
+
_inject_story_context() {
|
|
256
|
+
local story_json="$1"
|
|
261
257
|
|
|
262
258
|
echo ""
|
|
263
259
|
echo "---"
|
|
264
260
|
echo ""
|
|
265
261
|
echo "## Current Story"
|
|
266
262
|
echo ""
|
|
267
|
-
|
|
268
|
-
# Inject story details
|
|
269
|
-
local story_json
|
|
270
|
-
story_json=$(jq --arg id "$story" '.stories[] | select(.id==$id)' "$RALPH_DIR/prd.json")
|
|
271
263
|
echo '```json'
|
|
272
264
|
echo "$story_json"
|
|
273
265
|
echo '```'
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
# Helper: Inject file guidance into prompt
|
|
269
|
+
_inject_file_guidance() {
|
|
270
|
+
local story_json="$1"
|
|
274
271
|
|
|
275
|
-
# Extract file guidance if present
|
|
276
272
|
local has_files
|
|
277
273
|
has_files=$(echo "$story_json" | jq -r '.files // empty' 2>/dev/null)
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
274
|
+
[[ -z "$has_files" ]] && return
|
|
275
|
+
|
|
276
|
+
echo ""
|
|
277
|
+
echo "### File Guidance for This Story"
|
|
278
|
+
echo ""
|
|
279
|
+
echo "**Create these files:**"
|
|
280
|
+
echo "$story_json" | jq -r '.files.create[]? // empty' | sed 's/^/- /'
|
|
281
|
+
echo ""
|
|
282
|
+
echo "**Modify these files:**"
|
|
283
|
+
echo "$story_json" | jq -r '.files.modify[]? // empty' | sed 's/^/- /'
|
|
284
|
+
echo ""
|
|
285
|
+
echo "**Reuse/import from:**"
|
|
286
|
+
echo "$story_json" | jq -r '.files.reuse[]? // empty' | sed 's/^/- /'
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
# Helper: Inject scalability guidance for story
|
|
290
|
+
_inject_story_scale() {
|
|
291
|
+
local story_json="$1"
|
|
291
292
|
|
|
292
|
-
# Extract scalability guidance if present (for backend stories)
|
|
293
293
|
local has_scale
|
|
294
294
|
has_scale=$(echo "$story_json" | jq -r '.scale // empty' 2>/dev/null)
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
295
|
+
[[ -z "$has_scale" ]] && return
|
|
296
|
+
|
|
297
|
+
echo ""
|
|
298
|
+
echo "### Scalability Requirements for This Story"
|
|
299
|
+
echo ""
|
|
300
|
+
echo "$story_json" | jq -r '.scale | to_entries[] | "- **\(.key):** \(.value)"' 2>/dev/null
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
# Helper: Inject styleguide reference for frontend stories
|
|
304
|
+
_inject_styleguide() {
|
|
305
|
+
local story_json="$1"
|
|
301
306
|
|
|
302
|
-
# For frontend stories, instruct Claude to read the styleguide first
|
|
303
307
|
local story_type
|
|
304
308
|
story_type=$(echo "$story_json" | jq -r '.type // "frontend"' 2>/dev/null)
|
|
305
309
|
local styleguide_path
|
|
@@ -312,74 +316,87 @@ build_prompt() {
|
|
|
312
316
|
echo "**FIRST:** Read the project styleguide at \`$styleguide_path\` before implementing."
|
|
313
317
|
echo "Use existing components, colors, and patterns from the styleguide."
|
|
314
318
|
fi
|
|
319
|
+
}
|
|
315
320
|
|
|
321
|
+
# Helper: Inject feature-level context
|
|
322
|
+
_inject_feature_context() {
|
|
316
323
|
echo ""
|
|
317
324
|
echo "## Feature Context"
|
|
318
325
|
echo ""
|
|
319
326
|
echo '```json'
|
|
320
327
|
jq '{feature: .feature, metadata: .metadata}' "$RALPH_DIR/prd.json"
|
|
321
328
|
echo '```'
|
|
329
|
+
}
|
|
322
330
|
|
|
323
|
-
|
|
331
|
+
# Helper: Inject scalability requirements
|
|
332
|
+
_inject_scalability() {
|
|
324
333
|
local has_scalability
|
|
325
334
|
has_scalability=$(jq -r '.scalability // empty' "$RALPH_DIR/prd.json" 2>/dev/null)
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
335
|
+
[[ -z "$has_scalability" ]] && return
|
|
336
|
+
|
|
337
|
+
echo ""
|
|
338
|
+
echo "## Scalability Requirements"
|
|
339
|
+
echo ""
|
|
340
|
+
echo "**IMPORTANT:** Follow these scalability rules."
|
|
341
|
+
echo ""
|
|
342
|
+
echo '```json'
|
|
343
|
+
jq '.scalability' "$RALPH_DIR/prd.json"
|
|
344
|
+
echo '```'
|
|
345
|
+
echo ""
|
|
346
|
+
echo "### Key Rules:"
|
|
347
|
+
echo "- Always paginate list endpoints (never return unbounded arrays)"
|
|
348
|
+
echo "- Avoid N+1 queries - eager load relationships"
|
|
349
|
+
echo "- Add database indexes for frequently queried fields"
|
|
350
|
+
echo "- Implement caching strategy as specified"
|
|
351
|
+
echo "- Add rate limiting to public endpoints"
|
|
352
|
+
}
|
|
343
353
|
|
|
344
|
-
|
|
354
|
+
# Helper: Inject architecture guidelines
|
|
355
|
+
_inject_architecture() {
|
|
345
356
|
local has_architecture
|
|
346
357
|
has_architecture=$(jq -r '.architecture // empty' "$RALPH_DIR/prd.json" 2>/dev/null)
|
|
347
|
-
|
|
348
|
-
echo ""
|
|
349
|
-
echo "## Architecture Guidelines"
|
|
350
|
-
echo ""
|
|
351
|
-
echo "**IMPORTANT:** Follow these architecture rules strictly."
|
|
352
|
-
echo ""
|
|
353
|
-
echo '```json'
|
|
354
|
-
jq '.architecture' "$RALPH_DIR/prd.json"
|
|
355
|
-
echo '```'
|
|
356
|
-
echo ""
|
|
357
|
-
echo "### Key Rules:"
|
|
358
|
-
echo "- Put files in the specified directories"
|
|
359
|
-
echo "- Reuse existing components listed in 'patterns.reuse'"
|
|
360
|
-
echo "- Do NOT create anything in 'doNotCreate'"
|
|
361
|
-
echo "- Keep files under $(jq -r '.architecture.principles.maxFileLines // 300' "$RALPH_DIR/prd.json") lines"
|
|
362
|
-
echo "- Scripts go in scripts/, docs go in docs/"
|
|
363
|
-
fi
|
|
358
|
+
[[ -z "$has_architecture" ]] && return
|
|
364
359
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
360
|
+
echo ""
|
|
361
|
+
echo "## Architecture Guidelines"
|
|
362
|
+
echo ""
|
|
363
|
+
echo "**IMPORTANT:** Follow these architecture rules strictly."
|
|
364
|
+
echo ""
|
|
365
|
+
echo '```json'
|
|
366
|
+
jq '.architecture' "$RALPH_DIR/prd.json"
|
|
367
|
+
echo '```'
|
|
368
|
+
echo ""
|
|
369
|
+
echo "### Key Rules:"
|
|
370
|
+
echo "- Put files in the specified directories"
|
|
371
|
+
echo "- Reuse existing components listed in 'patterns.reuse'"
|
|
372
|
+
echo "- Do NOT create anything in 'doNotCreate'"
|
|
373
|
+
echo "- Keep files under $(jq -r '.architecture.principles.maxFileLines // 300' "$RALPH_DIR/prd.json") lines"
|
|
374
|
+
echo "- Scripts go in scripts/, docs go in docs/"
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
# Helper: Inject failure context from previous iteration
|
|
378
|
+
_inject_failure_context() {
|
|
379
|
+
local failure_context="$1"
|
|
380
|
+
[[ -z "$failure_context" ]] && return
|
|
381
|
+
|
|
382
|
+
echo ""
|
|
383
|
+
echo "## Previous Iteration Failed"
|
|
384
|
+
echo ""
|
|
385
|
+
echo "**IMPORTANT:** The previous attempt at this story failed verification. Review the errors below and fix them."
|
|
386
|
+
echo ""
|
|
387
|
+
echo '```'
|
|
388
|
+
echo "$failure_context"
|
|
389
|
+
echo '```'
|
|
390
|
+
echo ""
|
|
391
|
+
echo "### What to do:"
|
|
392
|
+
echo "1. Read the error messages carefully"
|
|
393
|
+
echo "2. Identify the root cause"
|
|
394
|
+
echo "3. Fix the issue (do not just retry the same approach)"
|
|
395
|
+
echo "4. Run verification again"
|
|
396
|
+
}
|
|
382
397
|
|
|
398
|
+
# Helper: Inject signs (learned patterns)
|
|
399
|
+
_inject_signs() {
|
|
383
400
|
echo ""
|
|
384
401
|
echo "## Signs (Learned Patterns)"
|
|
385
402
|
echo ""
|
|
@@ -390,16 +407,43 @@ build_prompt() {
|
|
|
390
407
|
else
|
|
391
408
|
echo "(none yet)"
|
|
392
409
|
fi
|
|
410
|
+
}
|
|
393
411
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
412
|
+
# Helper: Inject developer DNA
|
|
413
|
+
_inject_developer_dna() {
|
|
414
|
+
[[ ! -f "$HOME/.claude/DNA.md" ]] && return
|
|
415
|
+
|
|
416
|
+
echo ""
|
|
417
|
+
echo "## Developer DNA"
|
|
418
|
+
echo ""
|
|
419
|
+
echo "The developer has these working preferences:"
|
|
420
|
+
echo ""
|
|
421
|
+
cat "$HOME/.claude/DNA.md"
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
# Build the prompt with story context injected
|
|
425
|
+
build_prompt() {
|
|
426
|
+
local story="$1"
|
|
427
|
+
local failure_context="${2:-}"
|
|
428
|
+
|
|
429
|
+
# Read base PROMPT.md
|
|
430
|
+
cat "$PROMPT_FILE"
|
|
431
|
+
|
|
432
|
+
# Get story JSON once
|
|
433
|
+
local story_json
|
|
434
|
+
story_json=$(jq --arg id "$story" '.stories[] | select(.id==$id)' "$RALPH_DIR/prd.json")
|
|
435
|
+
|
|
436
|
+
# Inject all sections
|
|
437
|
+
_inject_story_context "$story_json"
|
|
438
|
+
_inject_file_guidance "$story_json"
|
|
439
|
+
_inject_story_scale "$story_json"
|
|
440
|
+
_inject_styleguide "$story_json"
|
|
441
|
+
_inject_feature_context
|
|
442
|
+
_inject_scalability
|
|
443
|
+
_inject_architecture
|
|
444
|
+
_inject_failure_context "$failure_context"
|
|
445
|
+
_inject_signs
|
|
446
|
+
_inject_developer_dna
|
|
403
447
|
}
|
|
404
448
|
|
|
405
449
|
# Mark feature as complete (keep PRD for appending new stories)
|
|
@@ -418,7 +462,14 @@ archive_feature() {
|
|
|
418
462
|
if ! git commit -m "feat: complete $feature_name" 2>/dev/null; then
|
|
419
463
|
# Retry after pre-commit auto-fixes
|
|
420
464
|
git add -A
|
|
421
|
-
git commit -m "feat: complete $feature_name" 2>/dev/null
|
|
465
|
+
if ! git commit -m "feat: complete $feature_name" 2>/dev/null; then
|
|
466
|
+
# Check if it's "nothing to commit" vs real error
|
|
467
|
+
if git diff --cached --quiet 2>/dev/null; then
|
|
468
|
+
echo " (no changes to commit)"
|
|
469
|
+
else
|
|
470
|
+
print_warning "Final commit failed - check git status"
|
|
471
|
+
fi
|
|
472
|
+
fi
|
|
422
473
|
fi
|
|
423
474
|
fi
|
|
424
475
|
|
package/ralph/utils.sh
CHANGED
|
@@ -6,9 +6,17 @@ readonly MAX_LOG_LINES=30
|
|
|
6
6
|
readonly MAX_PROGRESS_LINES=10
|
|
7
7
|
readonly MAX_GIT_STATUS_LINES=10
|
|
8
8
|
readonly MAX_OUTPUT_PREVIEW_LINES=20
|
|
9
|
+
readonly MAX_ERROR_PREVIEW_LINES=40
|
|
10
|
+
readonly MAX_LINT_ERROR_LINES=20
|
|
9
11
|
readonly ITERATION_DELAY_SECONDS=2
|
|
10
12
|
readonly DEFAULT_TIMEOUT_SECONDS=600
|
|
11
13
|
readonly DEFAULT_MAX_ITERATIONS=20
|
|
14
|
+
readonly CODE_REVIEW_TIMEOUT_SECONDS=120
|
|
15
|
+
readonly MAX_PROGRESS_FILE_LINES=1000
|
|
16
|
+
|
|
17
|
+
# Common project directories (avoid duplication across files)
|
|
18
|
+
readonly FRONTEND_DIRS=("apps/web" "frontend" "client" "web")
|
|
19
|
+
readonly BACKEND_DIRS=("apps/api" "api" "backend" "server")
|
|
12
20
|
|
|
13
21
|
# Track temp files for safe cleanup
|
|
14
22
|
RALPH_TEMP_FILES=()
|
|
@@ -20,6 +28,24 @@ YELLOW='\033[1;33m'
|
|
|
20
28
|
BLUE='\033[0;34m'
|
|
21
29
|
NC='\033[0m' # No Color
|
|
22
30
|
|
|
31
|
+
# Get existing frontend directories in this project
|
|
32
|
+
get_frontend_dirs() {
|
|
33
|
+
local dirs=()
|
|
34
|
+
for d in "${FRONTEND_DIRS[@]}"; do
|
|
35
|
+
[[ -d "$d" ]] && dirs+=("$d")
|
|
36
|
+
done
|
|
37
|
+
[[ ${#dirs[@]} -gt 0 ]] && printf '%s\n' "${dirs[@]}"
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
# Get existing backend directories in this project
|
|
41
|
+
get_backend_dirs() {
|
|
42
|
+
local dirs=()
|
|
43
|
+
for d in "${BACKEND_DIRS[@]}"; do
|
|
44
|
+
[[ -d "$d" ]] && dirs+=("$d")
|
|
45
|
+
done
|
|
46
|
+
[[ ${#dirs[@]} -gt 0 ]] && printf '%s\n' "${dirs[@]}"
|
|
47
|
+
}
|
|
48
|
+
|
|
23
49
|
# Progress bar for story display
|
|
24
50
|
progress_bar() {
|
|
25
51
|
local current=$1 total=$2 width=${3:-6}
|
|
@@ -95,14 +121,26 @@ check_dependencies() {
|
|
|
95
121
|
fi
|
|
96
122
|
}
|
|
97
123
|
|
|
98
|
-
# Log progress to progress.txt
|
|
124
|
+
# Log progress to progress.txt (with rotation to prevent unbounded growth)
|
|
99
125
|
log_progress() {
|
|
100
126
|
local story="$1"
|
|
101
127
|
local status="$2"
|
|
102
128
|
local msg="${3:-}"
|
|
103
129
|
local timestamp
|
|
130
|
+
local progress_file="$RALPH_DIR/progress.txt"
|
|
131
|
+
|
|
104
132
|
timestamp=$(date -Iseconds 2>/dev/null || date +%Y-%m-%dT%H:%M:%S)
|
|
105
|
-
echo "[$timestamp] $status $story $msg" >> "$
|
|
133
|
+
echo "[$timestamp] $status $story $msg" >> "$progress_file"
|
|
134
|
+
|
|
135
|
+
# Rotate if file exceeds max lines (keep last half)
|
|
136
|
+
if [[ -f "$progress_file" ]]; then
|
|
137
|
+
local line_count
|
|
138
|
+
line_count=$(wc -l < "$progress_file" 2>/dev/null || echo "0")
|
|
139
|
+
if [[ "$line_count" -gt "$MAX_PROGRESS_FILE_LINES" ]]; then
|
|
140
|
+
local keep_lines=$((MAX_PROGRESS_FILE_LINES / 2))
|
|
141
|
+
tail -"$keep_lines" "$progress_file" > "$progress_file.tmp" && mv "$progress_file.tmp" "$progress_file"
|
|
142
|
+
fi
|
|
143
|
+
fi
|
|
106
144
|
}
|
|
107
145
|
|
|
108
146
|
# Get a value from config.json with a default
|