agentic-loop 3.0.0 → 3.1.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/README.md CHANGED
@@ -2,237 +2,89 @@
2
2
 
3
3
  **Autonomous AI coding loop for Claude Code.**
4
4
 
5
- A toolkit for implementing [RALPH](https://ghuntley.com/ralph/) with [Claude Code](https://docs.anthropic.com/en/docs/claude-code) that helps you go from idea to shipped code.
5
+ You describe what you want to build. Claude Code writes a PRD (Product Requirements Document) with small, testable stories. Ralph executes each story automatically - coding, testing, and committing in a loop until everything passes.
6
6
 
7
- > **Optimized for:** Python, TypeScript, React, Go/Hugo, and Docker projects. Auto-detects ports from `docker-compose.yml`, Vite, Next.js, Hugo, FastAPI, and more.
8
-
9
- > You focus on what matters: your ideas. Brainstorm with `/idea`, then let Ralph handle the rest - coding, testing, and committing in an iterative loop until everything passes.
10
-
11
- ### This repo helps you customize your code output, run autonomous coding loops, and adds guardrails to ensure good code little and often:
12
- #### Customize
13
- - **`/my-dna`** - Add your own voice and values to your claude.md (This helps you personalize your experience and output)
14
- - **`/styleguide`** - Generate a UI component reference for consistent design (Stop AI from generating everything in purple add your unique style in an html and then reference it)
15
-
16
- #### Autonomous Code Loops with RALPH principles
17
- Run this in the terminal not in a claude cli session. It will use your claude subscription not an API key.
18
- - **`npx agentic-loop run`** - RALPH Autonomous loop: Brainstorm ideas → turn ideas into atomic prds → implement → verify → commit → repeat
19
- - **`npx agentic-loop run --fast`** - Fast mode: skips code review for quicker iterations (~2-3 min/story vs 5-8 min)
20
-
21
- #### Guardrails
22
- - **`/vibe-check`** - manually run the same automated tests and guardrail checks at any time
23
- - **`/review`** - manually run the same automated Security-focused code review with OWASP checks
24
- - **Pre-commit hooks** - Automatically block secrets, hardcoded URLs, and security issues with precommit hooks from entering into your git repo. Catch and fix issues before your CI/CD does
25
- - **Claude Code hooks** - Real-time warnings while coding (debug statements, secrets, hardcoded URLs)
7
+ > **Optimized for:** Python, TypeScript, React, Go/Hugo, and Docker projects.
26
8
 
27
9
  ---
28
- See More for on how the [RALPH loop](docs/RALPH.md) works in this repo
29
10
 
30
- ---
11
+ ## What It Does
12
+
13
+ **Brainstorm ideas with `/idea`**
14
+ Describe a feature in plain English. Claude asks clarifying questions, explores your codebase, and generates a PRD with atomic stories that can be implemented one at a time.
31
15
 
16
+ **Execute with Ralph**
17
+ Ralph reads the PRD and implements each story autonomously. It spawns Claude, runs verification (lint, tests, browser checks), and either commits on success or retries with error context on failure.
32
18
 
33
- ## Getting Started
19
+ **Customize your output**
20
+ - `/my-dna` - Add your voice and values so the code reflects your style
21
+ - `/styleguide` - Generate a UI component reference for consistent design
34
22
 
35
- ### Prerequisites
23
+ **Built-in guardrails**
24
+ - `/vibe-check`, `/review` - On-demand quality and security checks
25
+ - Pre-commit hooks - Block secrets, hardcoded URLs, debug statements
26
+ - Claude Code hooks - Real-time warnings while coding
36
27
 
37
- - Node.js 18+
38
- - [Claude Code](https://docs.anthropic.com/en/docs/claude-code) CLI installed and authenticated
39
- - `jq` (for config management): `brew install jq` or `apt install jq`
40
- - Optional: [Playwright](https://playwright.dev/) for browser verification (installed automatically by `/tour`)
28
+ ---
41
29
 
30
+ ## Quick Start
42
31
 
43
- ## Step 0: Install
32
+ **Prerequisites:** Node.js 18+, [Claude Code](https://docs.anthropic.com/en/docs/claude-code) CLI, `jq` (`brew install jq`)
44
33
 
45
34
  ```bash
46
- cd [your-project]
47
35
  npm install agentic-loop
48
36
  npx agentic-loop setup
49
37
  ```
50
38
 
51
- This sets up:
52
- - Slash commands (`/idea`, `/review`, etc.)
53
- - Pre-commit hooks (secrets, URLs, debug statements)
54
- - Claude Code hooks (real-time warnings)
55
- - Ralph configuration files
56
-
57
- > **Note:** Claude Code hooks require `jq`. On macOS with Homebrew, it's installed automatically. On Linux, if you see a warning, run `sudo apt install jq` (or your package manager) then `npx agentic-loop setup`.
58
-
59
- ## Step 1: Start claude (--dangerously-skip-permissions is optional)
60
-
39
+ **Terminal 1 - Claude Code:**
61
40
  ```bash
62
41
  claude --dangerously-skip-permissions
42
+ /tour # Guided walkthrough (recommended first time)
43
+ /idea 'your feature' # Generate a PRD
63
44
  ```
64
45
 
65
- ## Step 1b: Optional run tour and customizations
66
- Inside of a claude session run
67
- ```
68
- /tour
69
- ```
70
- 1. The tour auto-detects your project settings and walks you through the workflow.
71
- 2. You can set up your own styleguide for all your frontend work to reference. I highly recommend you do this.
72
- 3. run /my-dna to add your own writing style, core values, and things you want represented in your application so that your app represents you and what you want.
73
-
74
- ### Step 2: Brainstorm Your Idea
75
-
76
- ```
77
- /idea [describe your next feature]
78
- ```
79
-
80
- Claude asks clarifying questions, explores your codebase, then generates:
81
- - `docs/ideas/your-feature.md` - The documented idea
82
- - `.ralph/prd.json` - Atomic stories for Ralph
83
-
84
- Review and approve when prompted.
85
-
86
- > **Note:** If Claude skips writing to `docs/ideas/`, tell it: "Please write the idea to docs/ideas/ and generate the PRD as the /idea command specifies"
87
-
88
- ### Step 3: Execute with Ralph
89
-
90
- Type `/exit` or open a new terminal, then run:
91
-
46
+ **Terminal 2 - Ralph Loop:**
92
47
  ```bash
93
- # Standard mode (with code review)
94
- npx agentic-loop run
95
-
96
- # Fast mode (skip code review, ~2x faster)
97
- npx agentic-loop run --fast
98
-
99
- # Limit iterations
100
- npx agentic-loop run --max 10
48
+ npx agentic-loop run # Execute PRDs autonomously
49
+ npx agentic-loop run --fast # Skip code review (~2x faster)
101
50
  ```
102
51
 
103
- Ralph loops through stories one at a time, writes tests, verifies, and commits.
52
+ > **Tip:** Use two terminals. Plan with Claude in one, run Ralph in the other.
104
53
 
105
- > **Pro tip:** Use two terminals - plan with Claude in one, run Ralph in another.
106
- >
107
- > **Performance:** Fast mode skips the AI code review step, running lint and tests in parallel. Use it when iterating quickly on known-good patterns.
54
+ ---
108
55
 
109
- ## Ralph Details
56
+ ## How Ralph Works
110
57
 
111
58
  ```
112
59
  ┌─────────────────────────────────────────────────────────────┐
113
60
  │ RALPH LOOP │
114
61
  ├─────────────────────────────────────────────────────────────┤
115
- │ │
116
62
  │ 1. Read prd.json → find next story where passes=false │
117
63
  │ 2. Build prompt (story + context + failures + signs) │
118
64
  │ 3. Spawn Claude with prompt │
119
- │ 4. Run verification:
120
- │ - Code review (skipped in --fast mode) │
121
- │ - Lint + tests (run in parallel) │
122
- │ - Playwright/API tests │
123
- │ - Browser validation │
65
+ │ 4. Run verification (lint, tests, browser, code review)
124
66
  │ 5. Pass? → commit, next story │
125
67
  │ Fail? → save error, retry with failure context │
126
68
  │ 6. Repeat until all stories pass │
127
- │ │
128
69
  └─────────────────────────────────────────────────────────────┘
129
70
  ```
130
71
 
131
- ### Performance Options
132
-
133
- | Flag | Effect | Best For |
134
- |------|--------|----------|
135
- | `--fast` | Skip code review, parallel lint+tests | Rapid iteration, trusted patterns |
136
- | `--max N` | Limit to N iterations | Preventing runaway loops |
137
- | (default) | Full verification with code review | Production-quality code |
138
-
139
- ---
140
-
141
- ## Documentation
142
-
143
- | Doc | Description |
144
- |-----|-------------|
145
- | [How Ralph Works](docs/RALPH.md) | Architecture, config, troubleshooting |
146
- | [Cheatsheet](docs/CHEATSHEET.md) | All commands, quick reference |
147
- | [Workflow Guide](docs/WORKFLOW.md) | End-to-end development process |
148
- | [Hooks Reference](docs/HOOKS.md) | Pre-commit and Claude Code hooks |
149
- | [Bad Patterns](docs/BAD-PATTERNS.md) | AI code issues to avoid |
150
- | [Prompting Guide](docs/PROMPTING-GUIDE.md) | Writing effective PROMPT.md |
151
- | [TDD Guide](docs/TDD.md) | Test-driven development with Ralph |
152
- | [Styleguide](docs/STYLEGUIDE.md) | Creating UI component references |
153
- | [Contributing](docs/CONTRIBUTING.md) | How to contribute |
72
+ **What's a PRD?**
73
+ A JSON file (`.ralph/prd.json`) containing your feature broken into small stories. Each story has acceptance criteria, test steps, and a test URL. Ralph implements them one by one.
154
74
 
155
- ## Project Structure
156
-
157
- ```
158
- agentic-loop/
159
- ├── .claude/commands/ # Slash commands (/idea, /review, etc.)
160
- ├── ralph/ # Autonomous loop (Bash)
161
- │ ├── hooks/ # Claude Code hooks (real-time warnings)
162
- │ ├── browser-verify/ # Playwright verification
163
- │ └── setup/ # Setup wizards (/tour)
164
- ├── src/ # Pre-commit checks (TypeScript)
165
- │ └── checks/ # Check implementations (secrets, urls, etc.)
166
- ├── templates/ # Files for user projects
167
- │ ├── PROMPT.md # Base prompt for Ralph sessions
168
- │ ├── config/ # Project type configs (node, python)
169
- │ ├── optional/ # Extra configs (eslint, vscode, etc.)
170
- │ └── examples/ # Example CLAUDE.md files by stack
171
- ├── docs/ # Documentation
172
- └── bin/ # CLI entry points (agentic-loop, vibe-check)
173
- ```
174
-
175
- ### Three Layers of Quality Checks
176
-
177
- | Layer | When | What |
178
- |-------|------|------|
179
- | **Slash commands** | In Claude Code CLI | `/vibe-check`, `/review` - on-demand checks |
180
- | **Claude Code hooks** | While Claude writes code | Real-time warnings (debug statements, secrets) |
181
- | **Pre-commit hooks** | At `git commit` | Blocks secrets, URLs, debug statements |
182
-
183
- ---
184
-
185
- ## Troubleshooting
186
-
187
- ### "SessionStart: startup hook error"
188
-
189
- This means Claude Code hooks are configured but the hook scripts can't be found. Fix with:
190
-
191
- ```bash
192
- # Reinstall project hooks
193
- npx agentic-loop setup
194
- ```
195
-
196
- ### Hooks not working after moving project
197
-
198
- Claude Code hooks use absolute paths. After moving a project or cloning on a new machine, reinstall hooks:
199
-
200
- ```bash
201
- npx agentic-loop setup
202
- ```
203
-
204
- ### Team members getting hook errors
205
-
206
- `.claude/settings.json` contains machine-specific paths and should not be committed to git. The setup script automatically adds it to `.gitignore`. Each team member's hooks are configured when they run `npx agentic-loop setup`.
207
-
208
- If `.claude/settings.json` was accidentally committed:
209
- ```bash
210
- git rm --cached .claude/settings.json
211
- echo ".claude/settings.json" >> .gitignore
212
- git commit -m "Remove machine-specific settings from git"
213
- ```
214
-
215
- ### jq not installed
216
-
217
- Claude Code hooks require `jq` for JSON manipulation. On macOS with Homebrew, the setup installs it automatically. On Linux:
218
-
219
- ```bash
220
- # Ubuntu/Debian
221
- sudo apt install jq
222
-
223
- # Fedora/RHEL
224
- sudo dnf install jq
225
-
226
- # Then run setup
227
- npx agentic-loop setup
228
- ```
75
+ **What are Signs?**
76
+ Patterns Ralph learns from failures. If Ralph keeps making the same mistake, add a sign: `npx agentic-loop sign "Always use camelCase for API fields" backend`. Future stories will see this guidance.
229
77
 
230
78
  ---
231
79
 
232
- ## License
80
+ ## Docs
233
81
 
234
- MIT - see [LICENSE](LICENSE)
82
+ - [How Ralph Works](docs/RALPH.md) - Architecture, config, verification pipeline
83
+ - [Cheatsheet](docs/CHEATSHEET.md) - All commands at a glance
84
+ - [Hooks Reference](docs/HOOKS.md) - Pre-commit and Claude Code hooks
85
+ - [Troubleshooting](docs/TROUBLESHOOTING.md) - Common issues and fixes
86
+ - [Contributing](docs/CONTRIBUTING.md) - How to contribute
235
87
 
236
88
  ---
237
89
 
238
- Built by [AllThrive AI](https://allthrive.ai)
90
+ MIT License - [AllThrive AI](https://allthrive.ai)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-loop",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "Autonomous AI coding loop - PRD-driven development with Claude Code",
5
5
  "author": "Allie Jones <allie@allthrive.ai>",
6
6
  "license": "MIT",
package/ralph/init.sh CHANGED
@@ -196,7 +196,7 @@ auto_configure_project() {
196
196
  # Extract port from web/frontend service: "5173:5173" -> 5173
197
197
  web_port=$(grep -A 20 -E '^\s*(web|frontend|client|ui):' "$compose_file" 2>/dev/null | \
198
198
  grep -E '^\s*-\s*"?[0-9]+:[0-9]+"?' | head -1 | \
199
- grep -oE '[0-9]+:' | head -1 | tr -d ':')
199
+ grep -oE '[0-9]+:' | head -1 | tr -d ':' || true)
200
200
  fi
201
201
  done
202
202
 
@@ -206,7 +206,7 @@ auto_configure_project() {
206
206
  "apps/frontend/vite.config.ts" "frontend/vite.config.ts"; do
207
207
  if [[ -f "$vite_config" ]]; then
208
208
  # Look for port: 3000 or port: '3000'
209
- web_port=$(grep -E 'port\s*[=:]\s*[0-9]+' "$vite_config" 2>/dev/null | grep -oE '[0-9]{4}' | head -1)
209
+ web_port=$(grep -E 'port\s*[=:]\s*[0-9]+' "$vite_config" 2>/dev/null | grep -oE '[0-9]{4}' | head -1 || true)
210
210
  [[ -z "$web_port" ]] && web_port="5173" # Vite's documented default
211
211
  break
212
212
  fi
@@ -217,7 +217,7 @@ auto_configure_project() {
217
217
  if [[ -z "$web_port" ]]; then
218
218
  for hugo_config in "hugo.toml" "hugo.yaml" "hugo.json" "config.toml"; do
219
219
  if [[ -f "$hugo_config" ]] && [[ -d "content" || -d "layouts" || -d "themes" ]]; then
220
- web_port=$(grep -E 'port\s*=' "$hugo_config" 2>/dev/null | grep -oE '[0-9]+' | head -1)
220
+ web_port=$(grep -E 'port\s*=' "$hugo_config" 2>/dev/null | grep -oE '[0-9]+' | head -1 || true)
221
221
  [[ -z "$web_port" ]] && web_port="1313" # Hugo's documented default
222
222
  break
223
223
  fi
@@ -233,7 +233,7 @@ auto_configure_project() {
233
233
  local pkg_dir
234
234
  pkg_dir=$(dirname "$next_config")
235
235
  if [[ -f "$pkg_dir/package.json" ]]; then
236
- web_port=$(grep -E '"dev".*-p\s*[0-9]+' "$pkg_dir/package.json" 2>/dev/null | grep -oE '\-p\s*[0-9]+' | grep -oE '[0-9]+')
236
+ web_port=$(grep -E '"dev".*-p\s*[0-9]+' "$pkg_dir/package.json" 2>/dev/null | grep -oE '\-p\s*[0-9]+' | grep -oE '[0-9]+' || true)
237
237
  fi
238
238
  [[ -z "$web_port" ]] && web_port="3000" # Next.js documented default
239
239
  break
@@ -244,7 +244,7 @@ auto_configure_project() {
244
244
  # Priority 5: Generic package.json port detection
245
245
  if [[ -z "$web_port" && -f "package.json" ]]; then
246
246
  # Look for explicit port in scripts: --port 3000, -p 3000, :3000
247
- web_port=$(grep -E '"(dev|start|serve)"' package.json 2>/dev/null | grep -oE '(--port|-p|:)[[:space:]]*[0-9]{4}' | grep -oE '[0-9]{4}' | head -1)
247
+ web_port=$(grep -E '"(dev|start|serve)"' package.json 2>/dev/null | grep -oE '(--port|-p|:)[[:space:]]*[0-9]{4}' | grep -oE '[0-9]{4}' | head -1 || true)
248
248
  fi
249
249
 
250
250
  if [[ -n "$web_port" ]]; then
@@ -306,14 +306,14 @@ auto_configure_project() {
306
306
  # Extract port from api/backend service: "8000:8000" -> 8000
307
307
  api_port=$(grep -A 20 -E '^\s*(api|backend|server|app):' "$compose_file" 2>/dev/null | \
308
308
  grep -E '^\s*-\s*"?[0-9]+:[0-9]+"?' | head -1 | \
309
- grep -oE '[0-9]+:' | head -1 | tr -d ':')
309
+ grep -oE '[0-9]+:' | head -1 | tr -d ':' || true)
310
310
  fi
311
311
  done
312
312
 
313
313
  # Priority 2: Dockerfile EXPOSE directive
314
314
  if [[ -n "$backend_dir" && -z "$api_port" ]]; then
315
315
  if [[ -f "$backend_dir/Dockerfile" ]]; then
316
- api_port=$(grep -i "^EXPOSE" "$backend_dir/Dockerfile" 2>/dev/null | head -1 | grep -oE '[0-9]+' | head -1)
316
+ api_port=$(grep -i "^EXPOSE" "$backend_dir/Dockerfile" 2>/dev/null | head -1 | grep -oE '[0-9]+' | head -1 || true)
317
317
  fi
318
318
  fi
319
319
 
@@ -322,11 +322,11 @@ auto_configure_project() {
322
322
  if [[ -f "$backend_dir/pyproject.toml" ]] || [[ -f "$backend_dir/requirements.txt" ]]; then
323
323
  # Check pyproject.toml for port config
324
324
  if [[ -f "$backend_dir/pyproject.toml" ]]; then
325
- api_port=$(grep -E 'port\s*=' "$backend_dir/pyproject.toml" 2>/dev/null | grep -oE '[0-9]+' | head -1)
325
+ api_port=$(grep -E 'port\s*=' "$backend_dir/pyproject.toml" 2>/dev/null | grep -oE '[0-9]+' | head -1 || true)
326
326
  fi
327
327
  # Check for uvicorn command with --port
328
328
  if [[ -z "$api_port" && -f "$backend_dir/Makefile" ]]; then
329
- api_port=$(grep -E 'uvicorn.*--port' "$backend_dir/Makefile" 2>/dev/null | grep -oE '\-\-port[[:space:]]*[0-9]+' | grep -oE '[0-9]+' | head -1)
329
+ api_port=$(grep -E 'uvicorn.*--port' "$backend_dir/Makefile" 2>/dev/null | grep -oE '\-\-port[[:space:]]*[0-9]+' | grep -oE '[0-9]+' | head -1 || true)
330
330
  fi
331
331
  # Uvicorn/FastAPI default
332
332
  [[ -z "$api_port" ]] && api_port="8000"
@@ -335,7 +335,7 @@ auto_configure_project() {
335
335
 
336
336
  # Priority 4: Node backend - check package.json for port
337
337
  if [[ -n "$backend_dir" && -z "$api_port" && -f "$backend_dir/package.json" ]]; then
338
- api_port=$(grep -E '"(dev|start|serve)"' "$backend_dir/package.json" 2>/dev/null | grep -oE '(--port|-p|:)[[:space:]]*[0-9]{4}' | grep -oE '[0-9]{4}' | head -1)
338
+ api_port=$(grep -E '"(dev|start|serve)"' "$backend_dir/package.json" 2>/dev/null | grep -oE '(--port|-p|:)[[:space:]]*[0-9]{4}' | grep -oE '[0-9]{4}' | head -1 || true)
339
339
  fi
340
340
 
341
341
  if [[ -n "$api_port" ]]; then
@@ -415,7 +415,7 @@ ralph_status() {
415
415
  else
416
416
  # Check for misplaced PRD in subdirectories
417
417
  local found_prd
418
- found_prd=$(find . -path "./.ralph" -prune -o -name "prd.json" -path "*/.ralph/prd.json" -print 2>/dev/null | head -1)
418
+ found_prd=$(find . -path "./.ralph" -prune -o -name "prd.json" -path "*/.ralph/prd.json" -print 2>/dev/null | head -1 || true)
419
419
 
420
420
  if [[ -n "$found_prd" ]]; then
421
421
  print_warning "PRD found in wrong location: $found_prd"
package/ralph/loop.sh CHANGED
@@ -347,6 +347,11 @@ run_loop() {
347
347
  rm -f "$RALPH_DIR/last_migration_failure.log"
348
348
  rm -f "$RALPH_DIR/last_review_failure.json"
349
349
  rm -f "$RALPH_DIR/last_test_failure.log"
350
+ rm -f "$RALPH_DIR/last_lint_failure.log"
351
+ rm -f "$RALPH_DIR/last_typescript_failure.log"
352
+ rm -f "$RALPH_DIR/last_build_failure.log"
353
+ rm -f "$RALPH_DIR/last_go_failure.log"
354
+ rm -f "$RALPH_DIR/last_rust_failure.log"
350
355
  rm -f "$RALPH_DIR/last_playwright_failure.log"
351
356
  rm -f "$RALPH_DIR/last_browser_failure.json"
352
357
  rm -f "$RALPH_DIR/last_precommit_failure.log"
@@ -47,9 +47,13 @@ run_auto_fix() {
47
47
  fi
48
48
  }
49
49
 
50
- # Verify lint passes after auto-fix (catch unfixable errors)
50
+ # Verify lint passes after auto-fix (catch remaining errors that need manual fix)
51
51
  verify_lint() {
52
52
  local failed=0
53
+ local lint_log="$RALPH_DIR/last_lint_failure.log"
54
+
55
+ # Clear previous lint failure log
56
+ rm -f "$lint_log"
53
57
 
54
58
  # Python: ruff lint check
55
59
  if command -v ruff &>/dev/null && [[ -f "pyproject.toml" || -f "ruff.toml" ]]; then
@@ -59,8 +63,14 @@ verify_lint() {
59
63
  else
60
64
  print_error "failed"
61
65
  echo ""
62
- echo " Unfixable lint errors:"
63
- ruff check . 2>/dev/null | head -"$MAX_LINT_ERROR_LINES" | sed 's/^/ /'
66
+ echo " Lint errors (auto-fix couldn't resolve - Claude should fix these):"
67
+ local lint_output
68
+ lint_output=$(ruff check . 2>/dev/null | head -"$MAX_LINT_ERROR_LINES")
69
+ echo "$lint_output" | sed 's/^/ /'
70
+ {
71
+ echo "Lint errors in root directory:"
72
+ echo "$lint_output"
73
+ } >> "$lint_log"
64
74
  failed=1
65
75
  fi
66
76
  fi
@@ -78,16 +88,288 @@ verify_lint() {
78
88
  else
79
89
  print_error "failed"
80
90
  echo ""
81
- echo " Unfixable lint errors in $api_dir:"
82
- (cd "$api_dir" && ruff check . 2>/dev/null) | head -"$MAX_LINT_ERROR_LINES" | sed 's/^/ /'
91
+ echo " Lint errors in $api_dir (auto-fix couldn't resolve - Claude should fix these):"
92
+ local lint_output
93
+ lint_output=$(cd "$api_dir" && ruff check . 2>/dev/null | head -"$MAX_LINT_ERROR_LINES")
94
+ echo "$lint_output" | sed 's/^/ /'
95
+ {
96
+ echo ""
97
+ echo "Lint errors in $api_dir:"
98
+ echo "$lint_output"
99
+ } >> "$lint_log"
83
100
  failed=1
84
101
  fi
85
102
  fi
86
103
  done <<< "$api_dirs"
87
104
 
105
+ # JavaScript/TypeScript: ESLint check (root)
106
+ if [[ -f "package.json" ]] && command -v npx &>/dev/null; then
107
+ if grep -q '"eslint"' package.json 2>/dev/null || [[ -f ".eslintrc.js" ]] || [[ -f "eslint.config.js" ]]; then
108
+ echo -n " ESLint check... "
109
+ local eslint_output
110
+ if eslint_output=$(npx eslint . --max-warnings 0 2>&1); then
111
+ print_success "passed"
112
+ else
113
+ # Check if it's real errors or just warnings
114
+ if echo "$eslint_output" | grep -qE "✖ [0-9]+ problems? \([1-9]"; then
115
+ print_error "failed"
116
+ echo ""
117
+ echo " ESLint errors:"
118
+ echo "$eslint_output" | tail -"$MAX_LINT_ERROR_LINES" | sed 's/^/ /'
119
+ {
120
+ echo ""
121
+ echo "ESLint errors in root:"
122
+ echo "$eslint_output"
123
+ } >> "$lint_log"
124
+ failed=1
125
+ else
126
+ print_success "passed (warnings only)"
127
+ fi
128
+ fi
129
+ fi
130
+ fi
131
+
132
+ # Check frontend directories (monorepo support)
133
+ local fe_dirs
134
+ fe_dirs=$(get_frontend_dirs)
135
+
136
+ while IFS= read -r fe_dir; do
137
+ [[ -z "$fe_dir" ]] && continue
138
+ [[ ! -f "$fe_dir/package.json" ]] && continue
139
+ grep -q '"eslint"' "$fe_dir/package.json" 2>/dev/null || continue
140
+
141
+ echo -n " ESLint check ($fe_dir)... "
142
+ local eslint_output
143
+ if eslint_output=$(cd "$fe_dir" && npx eslint . --max-warnings 0 2>&1); then
144
+ print_success "passed"
145
+ else
146
+ if echo "$eslint_output" | grep -qE "✖ [0-9]+ problems? \([1-9]"; then
147
+ print_error "failed"
148
+ echo ""
149
+ echo " ESLint errors in $fe_dir:"
150
+ echo "$eslint_output" | tail -"$MAX_LINT_ERROR_LINES" | sed 's/^/ /'
151
+ {
152
+ echo ""
153
+ echo "ESLint errors in $fe_dir:"
154
+ echo "$eslint_output"
155
+ } >> "$lint_log"
156
+ failed=1
157
+ else
158
+ print_success "passed (warnings only)"
159
+ fi
160
+ fi
161
+ done <<< "$fe_dirs"
162
+
88
163
  return $failed
89
164
  }
90
165
 
166
+ # Verify TypeScript types compile
167
+ verify_typescript() {
168
+ local failed=0
169
+ local ts_log="$RALPH_DIR/last_typescript_failure.log"
170
+
171
+ # Clear previous failure log
172
+ rm -f "$ts_log"
173
+
174
+ # Check root tsconfig
175
+ if [[ -f "tsconfig.json" ]] && command -v npx &>/dev/null; then
176
+ echo -n " TypeScript typecheck... "
177
+ local ts_output
178
+ if ts_output=$(npx tsc --noEmit 2>&1); then
179
+ print_success "passed"
180
+ else
181
+ print_error "failed"
182
+ echo ""
183
+ echo " TypeScript errors:"
184
+ echo "$ts_output" | head -"$MAX_LINT_ERROR_LINES" | sed 's/^/ /'
185
+ # Save for retry context
186
+ {
187
+ echo "TypeScript errors in root:"
188
+ echo "$ts_output"
189
+ } >> "$ts_log"
190
+ failed=1
191
+ fi
192
+ fi
193
+
194
+ # Check frontend directories (monorepo support)
195
+ local fe_dirs
196
+ fe_dirs=$(get_frontend_dirs)
197
+
198
+ while IFS= read -r fe_dir; do
199
+ [[ -z "$fe_dir" ]] && continue
200
+ [[ ! -f "$fe_dir/tsconfig.json" ]] && continue
201
+
202
+ echo -n " TypeScript typecheck ($fe_dir)... "
203
+ local ts_output
204
+ if ts_output=$(cd "$fe_dir" && npx tsc --noEmit 2>&1); then
205
+ print_success "passed"
206
+ else
207
+ print_error "failed"
208
+ echo ""
209
+ echo " TypeScript errors in $fe_dir:"
210
+ echo "$ts_output" | head -"$MAX_LINT_ERROR_LINES" | sed 's/^/ /'
211
+ # Save for retry context
212
+ {
213
+ echo ""
214
+ echo "TypeScript errors in $fe_dir:"
215
+ echo "$ts_output"
216
+ } >> "$ts_log"
217
+ failed=1
218
+ fi
219
+ done <<< "$fe_dirs"
220
+
221
+ return $failed
222
+ }
223
+
224
+ # Verify npm build succeeds (catches bundling/SSR issues)
225
+ verify_build() {
226
+ local failed=0
227
+ local build_log="$RALPH_DIR/last_build_failure.log"
228
+
229
+ # Clear previous failure log
230
+ rm -f "$build_log"
231
+
232
+ # Check root package.json for build script
233
+ if [[ -f "package.json" ]] && grep -q '"build"' package.json 2>/dev/null; then
234
+ echo -n " npm build... "
235
+ local build_output
236
+ if build_output=$(npm run build 2>&1); then
237
+ print_success "passed"
238
+ else
239
+ print_error "failed"
240
+ echo ""
241
+ echo " Build errors:"
242
+ echo "$build_output" | tail -"$MAX_LINT_ERROR_LINES" | sed 's/^/ /'
243
+ # Save for retry context
244
+ {
245
+ echo "Build errors in root:"
246
+ echo "$build_output"
247
+ } >> "$build_log"
248
+ failed=1
249
+ fi
250
+ fi
251
+
252
+ # Check frontend directories (monorepo support)
253
+ local fe_dirs
254
+ fe_dirs=$(get_frontend_dirs)
255
+
256
+ while IFS= read -r fe_dir; do
257
+ [[ -z "$fe_dir" ]] && continue
258
+ [[ ! -f "$fe_dir/package.json" ]] && continue
259
+ # Skip if no build script
260
+ grep -q '"build"' "$fe_dir/package.json" 2>/dev/null || continue
261
+
262
+ echo -n " npm build ($fe_dir)... "
263
+ local build_output
264
+ if build_output=$(cd "$fe_dir" && npm run build 2>&1); then
265
+ print_success "passed"
266
+ else
267
+ print_error "failed"
268
+ echo ""
269
+ echo " Build errors in $fe_dir:"
270
+ echo "$build_output" | tail -"$MAX_LINT_ERROR_LINES" | sed 's/^/ /'
271
+ # Save for retry context
272
+ {
273
+ echo ""
274
+ echo "Build errors in $fe_dir:"
275
+ echo "$build_output"
276
+ } >> "$build_log"
277
+ failed=1
278
+ fi
279
+ done <<< "$fe_dirs"
280
+
281
+ return $failed
282
+ }
283
+
284
+ # Verify Go code compiles and passes vet
285
+ verify_go() {
286
+ local go_log="$RALPH_DIR/last_go_failure.log"
287
+
288
+ # Skip if not a Go project
289
+ [[ ! -f "go.mod" ]] && return 0
290
+ command -v go &>/dev/null || return 0
291
+
292
+ # Clear previous failure log
293
+ rm -f "$go_log"
294
+
295
+ # Go vet (catches common mistakes)
296
+ echo -n " Go vet... "
297
+ local vet_output
298
+ if vet_output=$(go vet ./... 2>&1); then
299
+ print_success "passed"
300
+ else
301
+ print_error "failed"
302
+ echo ""
303
+ echo " Go vet errors:"
304
+ echo "$vet_output" | head -"$MAX_LINT_ERROR_LINES" | sed 's/^/ /'
305
+ {
306
+ echo "Go vet errors:"
307
+ echo "$vet_output"
308
+ } >> "$go_log"
309
+ return 1
310
+ fi
311
+
312
+ # Go build (catches compile errors)
313
+ echo -n " Go build... "
314
+ local build_output
315
+ if build_output=$(go build ./... 2>&1); then
316
+ print_success "passed"
317
+ else
318
+ print_error "failed"
319
+ echo ""
320
+ echo " Go build errors:"
321
+ echo "$build_output" | head -"$MAX_LINT_ERROR_LINES" | sed 's/^/ /'
322
+ {
323
+ echo ""
324
+ echo "Go build errors:"
325
+ echo "$build_output"
326
+ } >> "$go_log"
327
+ return 1
328
+ fi
329
+
330
+ return 0
331
+ }
332
+
333
+ # Verify Rust code with clippy
334
+ verify_rust() {
335
+ local rust_log="$RALPH_DIR/last_rust_failure.log"
336
+
337
+ # Skip if not a Rust project
338
+ [[ ! -f "Cargo.toml" ]] && return 0
339
+ command -v cargo &>/dev/null || return 0
340
+
341
+ # Clear previous failure log
342
+ rm -f "$rust_log"
343
+
344
+ # Cargo clippy (Rust's official linter - catches more than cargo check)
345
+ echo -n " Cargo clippy... "
346
+ local clippy_output
347
+ if clippy_output=$(cargo clippy --all-targets --all-features -- -D warnings 2>&1); then
348
+ print_success "passed"
349
+ return 0
350
+ fi
351
+
352
+ # Check if clippy is installed
353
+ if echo "$clippy_output" | grep -q "can't find.*clippy"; then
354
+ echo -n "not installed, trying cargo check... "
355
+ if clippy_output=$(cargo check 2>&1); then
356
+ print_success "passed"
357
+ return 0
358
+ fi
359
+ fi
360
+
361
+ # Failed
362
+ print_error "failed"
363
+ echo ""
364
+ echo " Rust errors:"
365
+ echo "$clippy_output" | head -"$MAX_LINT_ERROR_LINES" | sed 's/^/ /'
366
+ {
367
+ echo "Rust errors:"
368
+ echo "$clippy_output"
369
+ } >> "$rust_log"
370
+ return 1
371
+ }
372
+
91
373
  # Check FastAPI endpoints have Pydantic response models (for Swagger docs)
92
374
  run_fastapi_response_check() {
93
375
  # Use RALPH_LIB which points to the ralph/ directory
@@ -137,205 +419,152 @@ run_fastapi_response_check() {
137
419
  return $failed
138
420
  }
139
421
 
140
- # Run all checks defined in config.json
141
- run_configured_checks() {
142
- local config="$RALPH_DIR/config.json"
143
-
144
- # ALWAYS run auto-fix and lint verification, even without config.json
145
- run_auto_fix
146
-
147
- # Verify lint passes after auto-fix (catch unfixable errors)
148
- if ! verify_lint; then
149
- return 1
422
+ # Check if a verification step is enabled in config
423
+ # Values: true, false, "final" (only on last story)
424
+ check_enabled() {
425
+ local check_name="$1"
426
+ local default="${2:-true}"
427
+ local value
428
+ value=$(get_config ".checks.$check_name" "$default")
429
+
430
+ # Handle "final" - only run on last story
431
+ if [[ "$value" == "final" ]]; then
432
+ local remaining
433
+ remaining=$(jq '[.stories[] | select(.passes==false)] | length' "$RALPH_DIR/prd.json" 2>/dev/null || echo "1")
434
+ [[ "$remaining" -eq 1 ]]
435
+ return
150
436
  fi
151
437
 
152
- # Auto-detect and run FastAPI response model check
153
- run_fastapi_response_check
154
-
155
- # Run pre-commit hooks if available (catches errors before commit attempt)
156
- if command -v pre-commit &>/dev/null && [[ -f ".pre-commit-config.yaml" ]]; then
157
- echo -n " pre-commit hooks... "
158
- local precommit_log="$RALPH_DIR/last_precommit_failure.log"
159
-
160
- # Helper function: check if pre-commit output has REAL errors (not just file modifications or warnings)
161
- has_real_errors() {
162
- local log_file="$1"
163
-
164
- # If all "Failed" hooks only have "files were modified" - not real errors
165
- # Real errors have patterns like: "error:", "Error:", numbered errors "✖ N problems (N errors"
166
- # But ESLint "0 errors, N warnings" is NOT a real error
167
-
168
- # Check for actual error indicators (not warnings-only)
169
- if grep -qE "^error:|: error:|Error:|SyntaxError|TypeError|NameError" "$log_file" 2>/dev/null; then
170
- return 0 # Has real errors
171
- fi
172
-
173
- # Check ESLint output - fail only if errors > 0
174
- if grep -qE "✖ [0-9]+ problems? \([1-9][0-9]* errors?" "$log_file" 2>/dev/null; then
175
- return 0 # Has real ESLint errors
176
- fi
177
-
178
- # Check ruff output - actual errors have file:line:col: error pattern
179
- if grep -qE "^[^:]+:[0-9]+:[0-9]+: [EF][0-9]+" "$log_file" 2>/dev/null; then
180
- return 0 # Has real ruff errors
181
- fi
182
-
183
- # Check for hooks that failed for reasons OTHER than file modification
184
- # Get all "Failed" hooks and check if any DON'T have "files were modified"
185
- local failed_hooks
186
- failed_hooks=$(grep -B 5 "^- hook id:" "$log_file" | grep -B 1 "Failed" | grep "hook id:" | sed 's/.*hook id: //' 2>/dev/null)
187
-
188
- while IFS= read -r hook_id; do
189
- [[ -z "$hook_id" ]] && continue
190
- # Check if this hook's failure section contains "files were modified"
191
- if ! grep -A 3 "hook id: $hook_id" "$log_file" | grep -q "files were modified"; then
192
- # This hook failed for a real reason
193
- return 0
194
- fi
195
- done <<< "$failed_hooks"
196
-
197
- return 1 # No real errors found
198
- }
199
-
200
- # Run pre-commit up to 3 times to handle auto-fix chains
201
- local max_attempts=3
202
- local attempt=1
203
- local passed=false
204
-
205
- while [[ $attempt -le $max_attempts ]]; do
206
- if pre-commit run --all-files > "$precommit_log" 2>&1; then
207
- passed=true
208
- break
209
- fi
438
+ [[ "$value" == "true" ]]
439
+ }
210
440
 
211
- # Check if failure is due to file modifications (auto-fix)
212
- if grep -q "files were modified by this hook" "$precommit_log"; then
213
- # Check if there are also REAL errors (not just file mods)
214
- if has_real_errors "$precommit_log"; then
215
- # Real errors exist - fail
216
- break
217
- fi
441
+ # Run all checks based on config.json flags
442
+ run_configured_checks() {
443
+ # ALWAYS run auto-fix (harmless, just formats code)
444
+ run_auto_fix
218
445
 
219
- # Only file modifications - stage and retry
220
- if [[ $attempt -lt $max_attempts ]]; then
221
- echo -n "auto-fixing (attempt $attempt)... "
222
- git add -A 2>/dev/null || true
223
- ((attempt++))
224
- continue
225
- else
226
- # Max attempts reached, but only file mods - consider it passed
227
- # Some hooks (like backup-database) always modify files
228
- echo -n "auto-fix complete... "
229
- git add -A 2>/dev/null || true
230
- passed=true
231
- break
232
- fi
233
- else
234
- # Failed without "files were modified" - check for real errors
235
- if has_real_errors "$precommit_log"; then
236
- break # Real errors
237
- else
238
- # No real errors detected (warnings only, etc.)
239
- passed=true
240
- break
241
- fi
242
- fi
446
+ # Lint check (ruff for Python, eslint for JS/TS)
447
+ if check_enabled "lint"; then
448
+ if ! verify_lint; then
449
+ return 1
450
+ fi
451
+ fi
243
452
 
244
- ((attempt++))
245
- done
453
+ # TypeScript type checking
454
+ if check_enabled "typecheck"; then
455
+ if ! verify_typescript; then
456
+ return 1
457
+ fi
458
+ fi
246
459
 
247
- if [[ "$passed" == "true" ]]; then
248
- if [[ $attempt -gt 1 ]]; then
249
- print_success "passed (after auto-fix)"
250
- else
251
- print_success "passed"
252
- fi
253
- rm -f "$precommit_log"
254
- else
255
- print_error "failed"
256
- echo ""
257
- echo " Pre-commit hook errors:"
258
- # Show actual errors, not just "Failed" status lines
259
- grep -E "^error:|: error:|Error:|SyntaxError|✖ [0-9]+ problems|^[^:]+:[0-9]+:[0-9]+:" "$precommit_log" | head -"$MAX_ERROR_PREVIEW_LINES" | sed 's/^/ /'
260
- # If no errors shown, show more context
261
- if ! grep -qE "^error:|: error:|Error:|SyntaxError|✖ [0-9]+ problems" "$precommit_log"; then
262
- echo " Full output:"
263
- tail -30 "$precommit_log" | sed 's/^/ /'
264
- fi
460
+ # Build verification (npm build, go build, cargo build)
461
+ if check_enabled "build"; then
462
+ if ! verify_build; then
463
+ return 1
464
+ fi
465
+ if ! verify_go; then
466
+ return 1
467
+ fi
468
+ if ! verify_rust; then
265
469
  return 1
266
470
  fi
267
471
  fi
268
472
 
269
- # Config-based checks are optional
270
- if [[ ! -f "$config" ]]; then
271
- echo " (no config.json for additional checks)"
272
- return 0
473
+ # FastAPI response model check
474
+ if check_enabled "fastapi" "false"; then
475
+ run_fastapi_response_check
273
476
  fi
274
477
 
275
- # Get list of check names (excluding 'test' which we run separately)
276
- local check_names
277
- check_names=$(jq -r '.checks | keys[] | select(. != "test")' "$config" 2>/dev/null)
478
+ # Run pre-commit hooks if available (catches errors before commit attempt)
479
+ run_precommit_hooks
278
480
 
279
- if [[ -z "$check_names" ]]; then
280
- echo " (no additional checks configured)"
281
- return 0
282
- fi
481
+ return 0
482
+ }
283
483
 
284
- local all_passed=0
484
+ # Run pre-commit hooks with auto-fix retry logic
485
+ run_precommit_hooks() {
486
+ # Skip if pre-commit not available
487
+ command -v pre-commit &>/dev/null || return 0
488
+ [[ -f ".pre-commit-config.yaml" ]] || return 0
285
489
 
286
- while IFS= read -r check_name; do
287
- [[ -z "$check_name" ]] && continue
490
+ echo -n " pre-commit hooks... "
491
+ local precommit_log="$RALPH_DIR/last_precommit_failure.log"
288
492
 
289
- local cmd
290
- cmd=$(jq -r ".checks[\"$check_name\"] // empty" "$config")
493
+ # Helper function: check if pre-commit output has REAL errors
494
+ has_real_errors() {
495
+ local log_file="$1"
291
496
 
292
- if [[ -z "$cmd" || "$cmd" == "null" ]]; then
293
- continue
497
+ # Check for actual error indicators (not warnings-only)
498
+ if grep -qE "^error:|: error:|Error:|SyntaxError|TypeError|NameError" "$log_file" 2>/dev/null; then
499
+ return 0
294
500
  fi
295
501
 
296
- # Check if command exists
297
- local first_word
298
- first_word=$(echo "$cmd" | awk '{print $1}')
502
+ # Check ESLint output - fail only if errors > 0
503
+ if grep -qE "✖ [0-9]+ problems? \([1-9][0-9]* errors?" "$log_file" 2>/dev/null; then
504
+ return 0
505
+ fi
299
506
 
300
- if [[ "$first_word" == "cd" ]]; then
301
- local actual_cmd
302
- actual_cmd=$(echo "$cmd" | sed 's/.*&& *//' | awk '{print $1}')
303
- if [[ -n "$actual_cmd" ]] && ! command -v "$actual_cmd" &>/dev/null; then
304
- echo " Skipping $check_name ($actual_cmd not found)"
305
- continue
306
- fi
307
- elif ! command -v "$first_word" &>/dev/null; then
308
- echo " Skipping $check_name ($first_word not found)"
309
- continue
507
+ # Check ruff output - actual errors have file:line:col: error pattern
508
+ if grep -qE "^[^:]+:[0-9]+:[0-9]+: [EF][0-9]+" "$log_file" 2>/dev/null; then
509
+ return 0
310
510
  fi
311
511
 
312
- if ! run_check "$check_name" "$cmd"; then
313
- all_passed=1
512
+ return 1
513
+ }
514
+
515
+ # Run pre-commit up to 3 times to handle auto-fix chains
516
+ local max_attempts=3
517
+ local attempt=1
518
+ local passed=false
519
+
520
+ while [[ $attempt -le $max_attempts ]]; do
521
+ if pre-commit run --all-files > "$precommit_log" 2>&1; then
522
+ passed=true
523
+ break
314
524
  fi
315
- done <<< "$check_names"
316
525
 
317
- return $all_passed
318
- }
526
+ # Check if failure is due to file modifications (auto-fix)
527
+ if grep -q "files were modified by this hook" "$precommit_log"; then
528
+ if has_real_errors "$precommit_log"; then
529
+ break # Real errors exist
530
+ fi
319
531
 
320
- # Run a single check
321
- run_check() {
322
- local name="$1"
323
- local cmd="$2"
324
- local log_file
325
- log_file=$(create_temp_file ".log") || return 1
532
+ # Only file modifications - stage and retry
533
+ if [[ $attempt -lt $max_attempts ]]; then
534
+ echo -n "auto-fixing (attempt $attempt)... "
535
+ git add -A 2>/dev/null || true
536
+ ((attempt++))
537
+ continue
538
+ else
539
+ git add -A 2>/dev/null || true
540
+ passed=true
541
+ break
542
+ fi
543
+ else
544
+ if has_real_errors "$precommit_log"; then
545
+ break
546
+ else
547
+ passed=true
548
+ break
549
+ fi
550
+ fi
326
551
 
327
- echo -n " $name... "
552
+ ((attempt++))
553
+ done
328
554
 
329
- if safe_exec "$cmd" "$log_file"; then
330
- print_success "passed"
331
- rm -f "$log_file"
555
+ if [[ "$passed" == "true" ]]; then
556
+ [[ $attempt -gt 1 ]] && print_success "passed (after auto-fix)" || print_success "passed"
557
+ rm -f "$precommit_log"
332
558
  return 0
333
559
  else
334
560
  print_error "failed"
335
561
  echo ""
336
- echo " Output (last $MAX_LOG_LINES lines):"
337
- tail -"$MAX_LOG_LINES" "$log_file" | sed 's/^/ /'
338
- rm -f "$log_file"
562
+ echo " Pre-commit hook errors:"
563
+ grep -E "^error:|: error:|Error:|SyntaxError|✖ [0-9]+ problems|^[^:]+:[0-9]+:[0-9]+:" "$precommit_log" 2>/dev/null | head -"$MAX_ERROR_PREVIEW_LINES" | sed 's/^/ /'
564
+ if ! grep -qE "^error:|: error:|Error:|SyntaxError|✖ [0-9]+ problems" "$precommit_log" 2>/dev/null; then
565
+ echo " Full output:"
566
+ tail -30 "$precommit_log" | sed 's/^/ /'
567
+ fi
339
568
  return 1
340
569
  fi
341
570
  }
package/ralph/verify.sh CHANGED
@@ -239,6 +239,72 @@ save_failure_context() {
239
239
  echo ""
240
240
  fi
241
241
 
242
+ if [[ -f "$RALPH_DIR/last_lint_failure.log" ]]; then
243
+ echo "--- Lint Failure ---"
244
+ echo "Fix these lint errors. The auto-fixer couldn't resolve them, but you can:"
245
+ echo ""
246
+ cat "$RALPH_DIR/last_lint_failure.log"
247
+ echo ""
248
+ echo "Common fixes:"
249
+ echo " - E722 bare except: Change 'except:' to 'except Exception:'"
250
+ echo " - F401 unused import: Remove the unused import"
251
+ echo " - F841 unused variable: Remove or use the variable"
252
+ echo ""
253
+ fi
254
+
255
+ if [[ -f "$RALPH_DIR/last_typescript_failure.log" ]]; then
256
+ echo "--- TypeScript Failure ---"
257
+ echo "Fix these TypeScript type errors:"
258
+ echo ""
259
+ cat "$RALPH_DIR/last_typescript_failure.log"
260
+ echo ""
261
+ echo "Common fixes:"
262
+ echo " - TS2322: Type mismatch - ensure assigned value matches expected type"
263
+ echo " - TS2339: Property not found - check spelling or add to interface"
264
+ echo " - TS2345: Argument type mismatch - cast or fix the argument type"
265
+ echo " - TS7006: Implicit any - add explicit type annotation"
266
+ echo ""
267
+ fi
268
+
269
+ if [[ -f "$RALPH_DIR/last_build_failure.log" ]]; then
270
+ echo "--- Build Failure ---"
271
+ echo "Fix these build errors:"
272
+ echo ""
273
+ cat "$RALPH_DIR/last_build_failure.log"
274
+ echo ""
275
+ echo "Common causes:"
276
+ echo " - Import errors: Check file paths and exports"
277
+ echo " - SSR issues (Next.js): Use dynamic imports for client-only code"
278
+ echo " - Missing dependencies: Run npm install"
279
+ echo ""
280
+ fi
281
+
282
+ if [[ -f "$RALPH_DIR/last_go_failure.log" ]]; then
283
+ echo "--- Go Failure ---"
284
+ echo "Fix these Go errors:"
285
+ echo ""
286
+ cat "$RALPH_DIR/last_go_failure.log"
287
+ echo ""
288
+ echo "Common fixes:"
289
+ echo " - Unused variable: Remove or use with _ prefix"
290
+ echo " - Undefined: Check imports and spelling"
291
+ echo " - Type mismatch: Ensure types align or use type assertion"
292
+ echo ""
293
+ fi
294
+
295
+ if [[ -f "$RALPH_DIR/last_rust_failure.log" ]]; then
296
+ echo "--- Rust Failure ---"
297
+ echo "Fix these Rust errors:"
298
+ echo ""
299
+ cat "$RALPH_DIR/last_rust_failure.log"
300
+ echo ""
301
+ echo "Common fixes:"
302
+ echo " - Unused variable: Prefix with _ or remove"
303
+ echo " - Borrow checker: Check ownership and lifetimes"
304
+ echo " - Missing trait: Add #[derive(...)] or impl"
305
+ echo ""
306
+ fi
307
+
242
308
  if [[ -f "$RALPH_DIR/last_precommit_failure.log" ]]; then
243
309
  echo "--- Pre-commit / Lint Failure ---"
244
310
  echo "Fix these errors before the story can be completed:"
@@ -37,11 +37,11 @@
37
37
  },
38
38
 
39
39
  "checks": {
40
- "typescript": "cd frontend && npx tsc --noEmit",
41
- "lint": "ruff check .",
42
- "lintFrontend": "cd frontend && npm run lint",
43
- "test": "python manage.py test --keepdb --parallel",
44
- "testFrontend": "cd frontend && npm test"
40
+ "lint": true,
41
+ "typecheck": true,
42
+ "build": "final",
43
+ "test": true,
44
+ "fastapi": true
45
45
  },
46
46
 
47
47
  "api": {
@@ -35,9 +35,11 @@
35
35
  },
36
36
 
37
37
  "checks": {
38
- "build": "go build ./...",
39
- "lint": "golangci-lint run",
40
- "test": "go test ./..."
38
+ "lint": true,
39
+ "typecheck": false,
40
+ "build": true,
41
+ "test": true,
42
+ "fastapi": false
41
43
  },
42
44
 
43
45
  "api": {
@@ -34,7 +34,13 @@
34
34
  "pattern": ""
35
35
  },
36
36
 
37
- "checks": {},
37
+ "checks": {
38
+ "lint": true,
39
+ "typecheck": true,
40
+ "build": "final",
41
+ "test": true,
42
+ "fastapi": false
43
+ },
38
44
 
39
45
  "api": {
40
46
  "baseUrl": "http://localhost:3000",
@@ -35,9 +35,11 @@
35
35
  },
36
36
 
37
37
  "checks": {
38
- "typescript": "npx tsc --noEmit",
39
- "lint": "npm run lint",
40
- "test": "npm test"
38
+ "lint": true,
39
+ "typecheck": true,
40
+ "build": "final",
41
+ "test": true,
42
+ "fastapi": false
41
43
  },
42
44
 
43
45
  "api": {
@@ -35,9 +35,11 @@
35
35
  },
36
36
 
37
37
  "checks": {
38
- "lint": "ruff check .",
39
- "typecheck": "mypy .",
40
- "test": "pytest"
38
+ "lint": true,
39
+ "typecheck": false,
40
+ "build": false,
41
+ "test": true,
42
+ "fastapi": true
41
43
  },
42
44
 
43
45
  "api": {
@@ -35,9 +35,11 @@
35
35
  },
36
36
 
37
37
  "checks": {
38
- "build": "cargo build",
39
- "lint": "cargo clippy -- -D warnings",
40
- "test": "cargo test"
38
+ "lint": true,
39
+ "typecheck": false,
40
+ "build": true,
41
+ "test": true,
42
+ "fastapi": false
41
43
  },
42
44
 
43
45
  "api": {