agentic-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.
Files changed (162) hide show
  1. package/.claude/commands/explain.md +114 -0
  2. package/.claude/commands/idea.md +398 -0
  3. package/.claude/commands/my-dna.md +122 -0
  4. package/.claude/commands/prd.md +286 -0
  5. package/.claude/commands/review.md +167 -0
  6. package/.claude/commands/sign.md +32 -0
  7. package/.claude/commands/styleguide.md +450 -0
  8. package/.claude/commands/tour.md +301 -0
  9. package/.claude/commands/vibe-check.md +116 -0
  10. package/.claude/commands/vibe-help.md +47 -0
  11. package/.claude/commands/vibe-list.md +203 -0
  12. package/.pre-commit-hooks.yaml +102 -0
  13. package/LICENSE +21 -0
  14. package/README.md +238 -0
  15. package/bin/agentic-loop.sh +24 -0
  16. package/bin/postinstall.sh +29 -0
  17. package/bin/ralph.sh +171 -0
  18. package/bin/vibe-check.js +19 -0
  19. package/dist/checks/check-any-types.d.ts +6 -0
  20. package/dist/checks/check-any-types.d.ts.map +1 -0
  21. package/dist/checks/check-any-types.js +73 -0
  22. package/dist/checks/check-any-types.js.map +1 -0
  23. package/dist/checks/check-commented-code.d.ts +6 -0
  24. package/dist/checks/check-commented-code.d.ts.map +1 -0
  25. package/dist/checks/check-commented-code.js +81 -0
  26. package/dist/checks/check-commented-code.js.map +1 -0
  27. package/dist/checks/check-console-error.d.ts +6 -0
  28. package/dist/checks/check-console-error.d.ts.map +1 -0
  29. package/dist/checks/check-console-error.js +41 -0
  30. package/dist/checks/check-console-error.js.map +1 -0
  31. package/dist/checks/check-debug-statements.d.ts +6 -0
  32. package/dist/checks/check-debug-statements.d.ts.map +1 -0
  33. package/dist/checks/check-debug-statements.js +120 -0
  34. package/dist/checks/check-debug-statements.js.map +1 -0
  35. package/dist/checks/check-deep-nesting.d.ts +6 -0
  36. package/dist/checks/check-deep-nesting.d.ts.map +1 -0
  37. package/dist/checks/check-deep-nesting.js +116 -0
  38. package/dist/checks/check-deep-nesting.js.map +1 -0
  39. package/dist/checks/check-docker-platform.d.ts +6 -0
  40. package/dist/checks/check-docker-platform.d.ts.map +1 -0
  41. package/dist/checks/check-docker-platform.js +42 -0
  42. package/dist/checks/check-docker-platform.js.map +1 -0
  43. package/dist/checks/check-dry-violations.d.ts +6 -0
  44. package/dist/checks/check-dry-violations.d.ts.map +1 -0
  45. package/dist/checks/check-dry-violations.js +124 -0
  46. package/dist/checks/check-dry-violations.js.map +1 -0
  47. package/dist/checks/check-empty-catch.d.ts +6 -0
  48. package/dist/checks/check-empty-catch.d.ts.map +1 -0
  49. package/dist/checks/check-empty-catch.js +111 -0
  50. package/dist/checks/check-empty-catch.js.map +1 -0
  51. package/dist/checks/check-function-length.d.ts +6 -0
  52. package/dist/checks/check-function-length.d.ts.map +1 -0
  53. package/dist/checks/check-function-length.js +152 -0
  54. package/dist/checks/check-function-length.js.map +1 -0
  55. package/dist/checks/check-hardcoded-ai-models.d.ts +10 -0
  56. package/dist/checks/check-hardcoded-ai-models.d.ts.map +1 -0
  57. package/dist/checks/check-hardcoded-ai-models.js +102 -0
  58. package/dist/checks/check-hardcoded-ai-models.js.map +1 -0
  59. package/dist/checks/check-hardcoded-urls.d.ts +6 -0
  60. package/dist/checks/check-hardcoded-urls.d.ts.map +1 -0
  61. package/dist/checks/check-hardcoded-urls.js +124 -0
  62. package/dist/checks/check-hardcoded-urls.js.map +1 -0
  63. package/dist/checks/check-magic-numbers.d.ts +6 -0
  64. package/dist/checks/check-magic-numbers.d.ts.map +1 -0
  65. package/dist/checks/check-magic-numbers.js +116 -0
  66. package/dist/checks/check-magic-numbers.js.map +1 -0
  67. package/dist/checks/check-secrets.d.ts +6 -0
  68. package/dist/checks/check-secrets.d.ts.map +1 -0
  69. package/dist/checks/check-secrets.js +138 -0
  70. package/dist/checks/check-secrets.js.map +1 -0
  71. package/dist/checks/check-snake-case-ts.d.ts +6 -0
  72. package/dist/checks/check-snake-case-ts.d.ts.map +1 -0
  73. package/dist/checks/check-snake-case-ts.js +78 -0
  74. package/dist/checks/check-snake-case-ts.js.map +1 -0
  75. package/dist/checks/check-todo-fixme.d.ts +6 -0
  76. package/dist/checks/check-todo-fixme.d.ts.map +1 -0
  77. package/dist/checks/check-todo-fixme.js +41 -0
  78. package/dist/checks/check-todo-fixme.js.map +1 -0
  79. package/dist/checks/check-unsafe-html.d.ts +6 -0
  80. package/dist/checks/check-unsafe-html.d.ts.map +1 -0
  81. package/dist/checks/check-unsafe-html.js +101 -0
  82. package/dist/checks/check-unsafe-html.js.map +1 -0
  83. package/dist/checks/index.d.ts +30 -0
  84. package/dist/checks/index.d.ts.map +1 -0
  85. package/dist/checks/index.js +57 -0
  86. package/dist/checks/index.js.map +1 -0
  87. package/dist/cli.d.ts +13 -0
  88. package/dist/cli.d.ts.map +1 -0
  89. package/dist/cli.js +208 -0
  90. package/dist/cli.js.map +1 -0
  91. package/dist/index.d.ts +9 -0
  92. package/dist/index.d.ts.map +1 -0
  93. package/dist/index.js +10 -0
  94. package/dist/index.js.map +1 -0
  95. package/dist/utils/file-reader.d.ts +24 -0
  96. package/dist/utils/file-reader.d.ts.map +1 -0
  97. package/dist/utils/file-reader.js +146 -0
  98. package/dist/utils/file-reader.js.map +1 -0
  99. package/dist/utils/patterns.d.ts +27 -0
  100. package/dist/utils/patterns.d.ts.map +1 -0
  101. package/dist/utils/patterns.js +84 -0
  102. package/dist/utils/patterns.js.map +1 -0
  103. package/dist/utils/reporters.d.ts +21 -0
  104. package/dist/utils/reporters.d.ts.map +1 -0
  105. package/dist/utils/reporters.js +115 -0
  106. package/dist/utils/reporters.js.map +1 -0
  107. package/dist/utils/types.d.ts +71 -0
  108. package/dist/utils/types.d.ts.map +1 -0
  109. package/dist/utils/types.js +5 -0
  110. package/dist/utils/types.js.map +1 -0
  111. package/package.json +83 -0
  112. package/ralph/api.sh +216 -0
  113. package/ralph/backup.sh +838 -0
  114. package/ralph/browser-verify/README.md +135 -0
  115. package/ralph/browser-verify/verify.ts +450 -0
  116. package/ralph/checks/check-fastapi-responses.py +155 -0
  117. package/ralph/hooks/hooks-config.json +72 -0
  118. package/ralph/hooks/inject-context.sh +44 -0
  119. package/ralph/hooks/install.sh +207 -0
  120. package/ralph/hooks/log-tools.sh +45 -0
  121. package/ralph/hooks/protect-prd.sh +27 -0
  122. package/ralph/hooks/save-learnings.sh +36 -0
  123. package/ralph/hooks/warn-debug.sh +54 -0
  124. package/ralph/hooks/warn-empty-catch.sh +63 -0
  125. package/ralph/hooks/warn-secrets.sh +89 -0
  126. package/ralph/hooks/warn-urls.sh +77 -0
  127. package/ralph/init.sh +515 -0
  128. package/ralph/loop.sh +730 -0
  129. package/ralph/playwright.sh +238 -0
  130. package/ralph/prd.sh +295 -0
  131. package/ralph/setup/feature-tour.sh +155 -0
  132. package/ralph/setup/quick-setup.sh +239 -0
  133. package/ralph/setup/tutorial.sh +159 -0
  134. package/ralph/setup/ui.sh +136 -0
  135. package/ralph/setup.sh +401 -0
  136. package/ralph/signs.sh +150 -0
  137. package/ralph/utils.sh +682 -0
  138. package/ralph/verify/browser.sh +324 -0
  139. package/ralph/verify/lint.sh +363 -0
  140. package/ralph/verify/review.sh +152 -0
  141. package/ralph/verify/tests.sh +81 -0
  142. package/ralph/verify.sh +268 -0
  143. package/templates/PROMPT.md +235 -0
  144. package/templates/config/fullstack.json +86 -0
  145. package/templates/config/go.json +81 -0
  146. package/templates/config/minimal.json +76 -0
  147. package/templates/config/node.json +81 -0
  148. package/templates/config/python.json +81 -0
  149. package/templates/config/rust.json +81 -0
  150. package/templates/examples/CLAUDE-django.md +174 -0
  151. package/templates/examples/CLAUDE-fastapi.md +270 -0
  152. package/templates/examples/CLAUDE-fastmcp.md +352 -0
  153. package/templates/examples/CLAUDE-fullstack.md +256 -0
  154. package/templates/examples/CLAUDE-node.md +246 -0
  155. package/templates/examples/CLAUDE-react.md +138 -0
  156. package/templates/optional/cursorrules.template +147 -0
  157. package/templates/optional/eslint.config.js +34 -0
  158. package/templates/optional/lint-staged.config.js +34 -0
  159. package/templates/optional/ruff.toml +125 -0
  160. package/templates/optional/vibe-check.yml +116 -0
  161. package/templates/optional/vscode-settings.json +127 -0
  162. package/templates/signs.json +46 -0
@@ -0,0 +1,152 @@
1
+ #!/usr/bin/env bash
2
+ # shellcheck shell=bash
3
+ # review.sh - Code review verification module for ralph
4
+
5
+ # Run code review on changes
6
+ run_code_review() {
7
+ local story="$1"
8
+
9
+ # Check if code review is enabled in config
10
+ local review_enabled
11
+ review_enabled=$(get_config '.verification.codeReviewEnabled' "true")
12
+ if [[ "$review_enabled" == "false" ]]; then
13
+ echo " (code review disabled in config, skipping)"
14
+ return 0
15
+ fi
16
+
17
+ # Check if git is available
18
+ if ! command -v git &>/dev/null || [[ ! -d ".git" ]]; then
19
+ echo " (no git repository, skipping)"
20
+ return 0
21
+ fi
22
+
23
+ # Get the diff of uncommitted changes (limit size to prevent memory issues)
24
+ local diff
25
+ local max_diff_lines=2000
26
+ diff=$(git diff HEAD 2>/dev/null | head -n "$max_diff_lines")
27
+
28
+ if [[ -z "$diff" ]]; then
29
+ # No uncommitted changes, check staged
30
+ diff=$(git diff --cached 2>/dev/null | head -n "$max_diff_lines")
31
+ fi
32
+
33
+ if [[ -z "$diff" ]]; then
34
+ echo " (no changes to review)"
35
+ return 0
36
+ fi
37
+
38
+ # Check if diff was truncated
39
+ local full_diff_lines
40
+ full_diff_lines=$(git diff HEAD 2>/dev/null | wc -l)
41
+ if [[ "$full_diff_lines" -gt "$max_diff_lines" ]]; then
42
+ echo " (diff truncated from $full_diff_lines to $max_diff_lines lines)"
43
+ fi
44
+
45
+ # Get story context for the review
46
+ local story_json
47
+ story_json=$(jq --arg id "$story" '.stories[] | select(.id==$id)' "$RALPH_DIR/prd.json" 2>/dev/null)
48
+
49
+ # Build the code review prompt
50
+ local prompt
51
+ prompt=$(cat <<EOF
52
+ You are a senior code reviewer. Review this diff for a story implementation.
53
+
54
+ ## Story Context
55
+ \`\`\`json
56
+ $story_json
57
+ \`\`\`
58
+
59
+ ## Code Diff
60
+ \`\`\`diff
61
+ $diff
62
+ \`\`\`
63
+
64
+ ## Review Checklist
65
+
66
+ Check for these issues:
67
+
68
+ 1. **Security** - SQL injection, XSS, command injection, hardcoded secrets, OWASP top 10
69
+ 2. **Error handling** - Missing try/catch, unhandled promise rejections, silent failures
70
+ 3. **Edge cases** - Null/undefined checks, empty arrays, boundary conditions
71
+ 4. **Code quality** - Unused variables, dead code, overly complex logic
72
+ 5. **Performance** - N+1 queries, unnecessary re-renders, memory leaks
73
+ 6. **Scalability** - Unbounded queries? Missing pagination? Missing indexes? No caching strategy?
74
+ 7. **Accessibility** - Missing ARIA labels, keyboard navigation, color contrast (if frontend)
75
+ 8. **Story compliance** - Does the code actually implement what the story requires?
76
+ 9. **Architecture** - Files in correct directories? Reusing existing components? File size < 300 lines?
77
+ 10. **No duplication** - Creating something that already exists? Reinventing utilities?
78
+
79
+ ## Response Format
80
+
81
+ IMPORTANT: Output ONLY raw JSON, no markdown formatting, no code blocks, no explanation.
82
+
83
+ {"pass": true/false, "issues": [{"severity": "critical|warning|info", "category": "security|error-handling|edge-case|quality|performance|scalability|a11y|architecture|compliance", "file": "path/to/file", "line": 123, "message": "Description", "suggestion": "Fix"}], "summary": "Brief assessment"}
84
+
85
+ Only fail (pass: false) for critical or multiple warning-level issues.
86
+ EOF
87
+ )
88
+
89
+ echo " Reviewing changes..."
90
+
91
+ local result
92
+ # Timeout for code review (defined in utils.sh)
93
+ result=$(echo "$prompt" | run_with_timeout "$CODE_REVIEW_TIMEOUT_SECONDS" claude -p --dangerously-skip-permissions 2>/dev/null) || {
94
+ print_warning " Code review skipped (Claude unavailable or timed out)"
95
+ return 0
96
+ }
97
+
98
+ # Save review result
99
+ mkdir -p "$RALPH_DIR/reviews"
100
+ echo "$result" > "$RALPH_DIR/reviews/${story}-review.json"
101
+
102
+ # Extract JSON from markdown code blocks if present
103
+ local json_result
104
+ if echo "$result" | grep -q '```json'; then
105
+ json_result=$(echo "$result" | sed -n '/```json/,/```/p' | sed '1d;$d')
106
+ elif echo "$result" | grep -q '```'; then
107
+ json_result=$(echo "$result" | sed -n '/```/,/```/p' | sed '1d;$d')
108
+ else
109
+ json_result="$result"
110
+ fi
111
+
112
+ # Check if result is valid JSON
113
+ if ! echo "$json_result" | jq -e . >/dev/null 2>&1; then
114
+ print_warning " Code review returned invalid response, skipping"
115
+ return 0
116
+ fi
117
+
118
+ local passed
119
+ passed=$(echo "$json_result" | jq -r '.pass // true' 2>/dev/null)
120
+
121
+ # Handle empty/null result
122
+ if [[ -z "$passed" || "$passed" == "null" ]]; then
123
+ print_warning " Code review inconclusive, continuing"
124
+ return 0
125
+ fi
126
+
127
+ if [[ "$passed" == "true" ]]; then
128
+ print_success "passed"
129
+
130
+ # Show any warnings/info even on pass
131
+ local warnings
132
+ warnings=$(echo "$json_result" | jq -r '.issues[] | select(.severity != "critical") | " [\(.severity)] \(.message)"' 2>/dev/null)
133
+ if [[ -n "$warnings" ]]; then
134
+ echo " Notes:"
135
+ echo "$warnings"
136
+ fi
137
+ return 0
138
+ else
139
+ print_error "failed"
140
+ echo ""
141
+
142
+ # Show all issues
143
+ echo " Issues found:"
144
+ echo "$json_result" | jq -r '.issues[] | " [\(.severity)] \(.category): \(.message)"' 2>/dev/null
145
+ echo ""
146
+ echo " Summary: $(echo "$json_result" | jq -r '.summary // "Review failed"' 2>/dev/null)"
147
+
148
+ # Save for failure context
149
+ echo "$json_result" > "$RALPH_DIR/last_review_failure.json"
150
+ return 1
151
+ fi
152
+ }
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env bash
2
+ # shellcheck shell=bash
3
+ # tests.sh - Test verification module for ralph
4
+
5
+ # Run unit tests
6
+ run_unit_tests() {
7
+ local log_file
8
+ log_file=$(create_temp_file ".log") || return 1
9
+
10
+ # Try common test commands
11
+ local test_cmd
12
+ test_cmd=$(get_config '.checks.test' "")
13
+
14
+ if [[ -z "$test_cmd" ]]; then
15
+ # Auto-detect test command
16
+ if [[ -f "package.json" ]] && grep -q '"test"' package.json; then
17
+ test_cmd="npm test"
18
+ elif [[ -f "pytest.ini" ]] || [[ -f "pyproject.toml" ]]; then
19
+ test_cmd="pytest"
20
+ elif [[ -f "Cargo.toml" ]]; then
21
+ test_cmd="cargo test"
22
+ elif [[ -f "go.mod" ]]; then
23
+ test_cmd="go test ./..."
24
+ else
25
+ echo " (no test command found, skipping)"
26
+ return 0
27
+ fi
28
+ fi
29
+
30
+ echo -n " Running: $test_cmd... "
31
+
32
+ if safe_exec "$test_cmd" "$log_file"; then
33
+ print_success "passed"
34
+ rm -f "$log_file"
35
+ return 0
36
+ else
37
+ print_error "failed"
38
+ echo ""
39
+ echo " Output (last $MAX_LOG_LINES lines):"
40
+ tail -"$MAX_LOG_LINES" "$log_file" | sed 's/^/ /'
41
+ cp "$log_file" "$RALPH_DIR/last_test_failure.log"
42
+ rm -f "$log_file"
43
+ return 1
44
+ fi
45
+ }
46
+
47
+ # Verify PRD acceptance criteria / test steps
48
+ verify_prd_criteria() {
49
+ local story="$1"
50
+
51
+ local test_steps
52
+ test_steps=$(jq -r --arg id "$story" '.stories[] | select(.id==$id) | .testSteps[]?' "$RALPH_DIR/prd.json" 2>/dev/null)
53
+
54
+ if [[ -z "$test_steps" ]]; then
55
+ echo " (no testSteps defined)"
56
+ return 0
57
+ fi
58
+
59
+ local failed=0
60
+ local log_file
61
+ log_file=$(create_temp_file ".log") || return 1
62
+
63
+ while IFS= read -r step; do
64
+ [[ -z "$step" ]] && continue
65
+
66
+ echo -n " $step... "
67
+
68
+ if safe_exec "$step" "$log_file"; then
69
+ print_success "passed"
70
+ else
71
+ print_error "failed"
72
+ echo ""
73
+ echo " Output:"
74
+ tail -"$MAX_OUTPUT_PREVIEW_LINES" "$log_file" | sed 's/^/ /'
75
+ failed=1
76
+ fi
77
+ done <<< "$test_steps"
78
+
79
+ rm -f "$log_file"
80
+ return $failed
81
+ }
@@ -0,0 +1,268 @@
1
+ #!/usr/bin/env bash
2
+ # shellcheck shell=bash
3
+ # verify.sh - Full UAT verification pipeline for ralph
4
+ #
5
+ # This file orchestrates the verification pipeline by sourcing modular components:
6
+ # - verify/review.sh - Code review logic
7
+ # - verify/lint.sh - Auto-fix + lint checks
8
+ # - verify/tests.sh - Unit tests + PRD criteria
9
+ # - verify/browser.sh - Browser validation
10
+
11
+ # Validate required source files exist before sourcing
12
+ if [[ ! -f "$RALPH_LIB/playwright.sh" ]]; then
13
+ echo "Error: Missing $RALPH_LIB/playwright.sh" >&2
14
+ exit 1
15
+ fi
16
+ if [[ ! -f "$RALPH_LIB/api.sh" ]]; then
17
+ echo "Error: Missing $RALPH_LIB/api.sh" >&2
18
+ exit 1
19
+ fi
20
+
21
+ # Source verification modules
22
+ source "$RALPH_LIB/playwright.sh"
23
+ source "$RALPH_LIB/api.sh"
24
+
25
+ # Determine the directory where this script lives
26
+ VERIFY_DIR="${RALPH_LIB:-$(dirname "${BASH_SOURCE[0]}")}"
27
+
28
+ # Source modular verification components
29
+ source "$VERIFY_DIR/verify/review.sh"
30
+ source "$VERIFY_DIR/verify/lint.sh"
31
+ source "$VERIFY_DIR/verify/tests.sh"
32
+ source "$VERIFY_DIR/verify/browser.sh"
33
+
34
+ run_verification() {
35
+ local story="$1"
36
+
37
+ echo ""
38
+ print_info "=== Verification: $story ==="
39
+ echo ""
40
+
41
+ # Clear old failure logs (so smart-skip logic uses fresh state)
42
+ rm -f "$RALPH_DIR/last_precommit_failure.log" \
43
+ "$RALPH_DIR/last_test_failure.log" \
44
+ "$RALPH_DIR/last_review_failure.json" 2>/dev/null
45
+
46
+ # Check for fast mode
47
+ local fast_mode="${RALPH_FAST_MODE:-false}"
48
+ if [[ "$fast_mode" == "true" ]]; then
49
+ echo " (fast mode - skipping code review)"
50
+ fi
51
+
52
+ # Determine story type (single jq call for all story info)
53
+ local story_json
54
+ story_json=$(jq --arg id "$story" '.stories[] | select(.id==$id)' "$RALPH_DIR/prd.json" 2>/dev/null)
55
+
56
+ local story_type has_test_url has_api_endpoints
57
+ story_type=$(echo "$story_json" | jq -r '.type // "frontend"')
58
+ has_test_url=$(echo "$story_json" | jq -r '.testUrl // empty')
59
+ has_api_endpoints=$(echo "$story_json" | jq -r '.apiEndpoints[0] // empty')
60
+
61
+ # Auto-detect type if not specified
62
+ if [[ -n "$has_api_endpoints" && -z "$has_test_url" ]]; then
63
+ story_type="backend"
64
+ fi
65
+
66
+ local failed=0
67
+ local lint_failed=0
68
+ local test_failed=0
69
+
70
+ # ========================================
71
+ # STEP 1: Code review (skip in fast mode or if last failure was lint/test)
72
+ # ========================================
73
+ local skip_review=false
74
+ if [[ "$fast_mode" == "true" ]]; then
75
+ skip_review=true
76
+ elif [[ -f "$RALPH_DIR/last_precommit_failure.log" ]] || [[ -f "$RALPH_DIR/last_test_failure.log" ]]; then
77
+ # Skip review if last failure was lint/test - review won't help
78
+ skip_review=true
79
+ echo " [1/5] Skipping code review (last failure was lint/test)"
80
+ fi
81
+
82
+ if [[ "$skip_review" == "false" ]]; then
83
+ echo " [1/5] Running code review..."
84
+ if ! run_code_review "$story"; then
85
+ failed=1
86
+ fi
87
+ fi
88
+
89
+ # ========================================
90
+ # STEP 2+3: Run lint and tests IN PARALLEL
91
+ # ========================================
92
+ if [[ $failed -eq 0 ]]; then
93
+ echo ""
94
+ echo " [2/5] Running lint + tests (parallel)..."
95
+
96
+ # Create temp files for results
97
+ local lint_log test_log
98
+ lint_log=$(mktemp)
99
+ test_log=$(mktemp)
100
+
101
+ # Run lint in background
102
+ (run_configured_checks > "$lint_log" 2>&1; echo $? > "${lint_log}.exit") &
103
+ local lint_pid=$!
104
+
105
+ # Run tests in background
106
+ (run_unit_tests > "$test_log" 2>&1; echo $? > "${test_log}.exit") &
107
+ local test_pid=$!
108
+
109
+ # Wait for both
110
+ wait $lint_pid 2>/dev/null
111
+ wait $test_pid 2>/dev/null
112
+
113
+ # Check results
114
+ lint_failed=$(cat "${lint_log}.exit" 2>/dev/null || echo "1")
115
+ test_failed=$(cat "${test_log}.exit" 2>/dev/null || echo "1")
116
+
117
+ # Show lint output
118
+ echo " Lint:"
119
+ cat "$lint_log" | sed 's/^/ /'
120
+
121
+ # Show test output
122
+ echo " Tests:"
123
+ cat "$test_log" | sed 's/^/ /'
124
+
125
+ # Cleanup
126
+ rm -f "$lint_log" "${lint_log}.exit" "$test_log" "${test_log}.exit"
127
+
128
+ if [[ "$lint_failed" != "0" ]] || [[ "$test_failed" != "0" ]]; then
129
+ failed=1
130
+ fi
131
+ fi
132
+
133
+ # ========================================
134
+ # STEP 3: Run Playwright tests (frontend) or API tests (backend)
135
+ # ========================================
136
+ if [[ $failed -eq 0 ]]; then
137
+ echo ""
138
+ if [[ "$story_type" == "backend" ]]; then
139
+ echo " [3/5] Running API tests..."
140
+ if ! run_api_validation "$story"; then
141
+ failed=1
142
+ elif ! run_api_error_tests "$story"; then
143
+ failed=1
144
+ fi
145
+ else
146
+ echo " [3/5] Running Playwright tests..."
147
+ if ! run_playwright_tests "$story"; then
148
+ failed=1
149
+ fi
150
+ fi
151
+ fi
152
+
153
+ # ========================================
154
+ # STEP 4: Run browser validation (frontend) or API validation (backend)
155
+ # ========================================
156
+ if [[ $failed -eq 0 ]]; then
157
+ echo ""
158
+ if [[ "$story_type" == "backend" ]]; then
159
+ echo " [4/5] Running API validation..."
160
+ if ! run_api_tests "$story"; then
161
+ failed=1
162
+ fi
163
+ else
164
+ echo " [4/5] Running browser validation..."
165
+ if ! run_browser_validation "$story"; then
166
+ failed=1
167
+ fi
168
+ fi
169
+ fi
170
+
171
+ # ========================================
172
+ # STEP 5: Run PRD test steps
173
+ # ========================================
174
+ if [[ $failed -eq 0 ]]; then
175
+ echo ""
176
+ echo " [5/5] Running PRD test steps..."
177
+ if ! verify_prd_criteria "$story"; then
178
+ failed=1
179
+ fi
180
+ fi
181
+
182
+ # ========================================
183
+ # Final result
184
+ # ========================================
185
+ echo ""
186
+ if [[ $failed -eq 0 ]]; then
187
+ print_success "=== All verification passed ==="
188
+ return 0
189
+ else
190
+ print_error "=== Verification failed ==="
191
+ # Save failure context for next iteration
192
+ save_failure_context "$story"
193
+ return 1
194
+ fi
195
+ }
196
+
197
+ # Save failure context for next iteration
198
+ save_failure_context() {
199
+ local story="$1"
200
+
201
+ local context_file="$RALPH_DIR/last_failure.txt"
202
+
203
+ {
204
+ echo "=== Failure Context for $story ==="
205
+ echo "Timestamp: $(date -Iseconds 2>/dev/null || date)"
206
+ echo ""
207
+
208
+ if [[ -f "$RALPH_DIR/last_migration_failure.log" ]]; then
209
+ echo "--- Migration Failure ---"
210
+ echo "Database migrations failed. Fix the migration or the code causing it:"
211
+ tail -50 "$RALPH_DIR/last_migration_failure.log"
212
+ echo ""
213
+ fi
214
+
215
+ if [[ -f "$RALPH_DIR/last_review_failure.json" ]]; then
216
+ echo "--- Code Review Failure ---"
217
+ echo "Issues found by code review:"
218
+ jq -r '.issues[] | "- [\(.severity)] \(.category): \(.message)\n File: \(.file // "unknown"):\(.line // "?")\n Fix: \(.suggestion // "See above")"' "$RALPH_DIR/last_review_failure.json" 2>/dev/null
219
+ echo ""
220
+ fi
221
+
222
+ if [[ -f "$RALPH_DIR/last_test_failure.log" ]]; then
223
+ echo "--- Test Failure ---"
224
+ tail -50 "$RALPH_DIR/last_test_failure.log"
225
+ echo ""
226
+ fi
227
+
228
+ if [[ -f "$RALPH_DIR/last_playwright_failure.log" ]]; then
229
+ echo "--- Playwright Failure ---"
230
+ tail -50 "$RALPH_DIR/last_playwright_failure.log"
231
+ echo ""
232
+ fi
233
+
234
+ if [[ -f "$RALPH_DIR/last_browser_failure.json" ]]; then
235
+ echo "--- Browser Validation Failure ---"
236
+ jq -r '"Errors: " + (.errors | join(", "))' "$RALPH_DIR/last_browser_failure.json" 2>/dev/null
237
+ jq -r '"Console errors: " + (.consoleErrors | join(", "))' "$RALPH_DIR/last_browser_failure.json" 2>/dev/null
238
+ jq -r '"Missing elements: " + (.elementsMissing | join(", "))' "$RALPH_DIR/last_browser_failure.json" 2>/dev/null
239
+ echo ""
240
+ fi
241
+
242
+ if [[ -f "$RALPH_DIR/last_precommit_failure.log" ]]; then
243
+ echo "--- Pre-commit / Lint Failure ---"
244
+ echo "Fix these errors before the story can be completed:"
245
+ echo ""
246
+ # Extract actual error lines (not warnings-only or file modification messages)
247
+ grep -E "^error:|: error:|Error:|SyntaxError|✖ [0-9]+ problems|^[^:]+:[0-9]+:[0-9]+: [EF][0-9]+" "$RALPH_DIR/last_precommit_failure.log" | head -30
248
+ # If no errors shown, show the full log tail
249
+ if ! grep -qE "^error:|: error:|Error:|SyntaxError|✖ [0-9]+ problems" "$RALPH_DIR/last_precommit_failure.log"; then
250
+ echo "(Full output):"
251
+ tail -40 "$RALPH_DIR/last_precommit_failure.log"
252
+ fi
253
+ echo ""
254
+ fi
255
+
256
+ if [[ -f "$RALPH_DIR/last_fastapi_response_check.log" ]]; then
257
+ echo "--- FastAPI Response Model Failure ---"
258
+ echo "Add Pydantic response_model to these endpoints for proper Swagger docs:"
259
+ echo ""
260
+ cat "$RALPH_DIR/last_fastapi_response_check.log"
261
+ echo ""
262
+ echo "Fix by adding response_model parameter or return type annotation:"
263
+ echo ' @router.get("/items", response_model=list[ItemSchema])'
264
+ echo " async def get_items() -> list[ItemSchema]:"
265
+ echo ""
266
+ fi
267
+ } > "$context_file"
268
+ }