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 +39 -187
- package/package.json +1 -1
- package/ralph/init.sh +11 -11
- package/ralph/loop.sh +5 -0
- package/ralph/verify/lint.sh +402 -173
- package/ralph/verify.sh +66 -0
- package/templates/config/fullstack.json +5 -5
- package/templates/config/go.json +5 -3
- package/templates/config/minimal.json +7 -1
- package/templates/config/node.json +5 -3
- package/templates/config/python.json +5 -3
- package/templates/config/rust.json +5 -3
package/README.md
CHANGED
|
@@ -2,237 +2,89 @@
|
|
|
2
2
|
|
|
3
3
|
**Autonomous AI coding loop for Claude Code.**
|
|
4
4
|
|
|
5
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
52
|
+
> **Tip:** Use two terminals. Plan with Claude in one, run Ralph in the other.
|
|
104
53
|
|
|
105
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
80
|
+
## Docs
|
|
233
81
|
|
|
234
|
-
|
|
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
|
-
|
|
90
|
+
MIT License - [AllThrive AI](https://allthrive.ai)
|
package/package.json
CHANGED
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"
|
package/ralph/verify/lint.sh
CHANGED
|
@@ -47,9 +47,13 @@ run_auto_fix() {
|
|
|
47
47
|
fi
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
# Verify lint passes after auto-fix (catch
|
|
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 "
|
|
63
|
-
|
|
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 "
|
|
82
|
-
|
|
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
|
-
#
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
|
|
153
|
-
|
|
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
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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
|
-
|
|
245
|
-
|
|
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
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
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
|
-
#
|
|
270
|
-
if
|
|
271
|
-
|
|
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
|
-
#
|
|
276
|
-
|
|
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
|
-
|
|
280
|
-
|
|
281
|
-
return 0
|
|
282
|
-
fi
|
|
481
|
+
return 0
|
|
482
|
+
}
|
|
283
483
|
|
|
284
|
-
|
|
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
|
-
|
|
287
|
-
|
|
490
|
+
echo -n " pre-commit hooks... "
|
|
491
|
+
local precommit_log="$RALPH_DIR/last_precommit_failure.log"
|
|
288
492
|
|
|
289
|
-
|
|
290
|
-
|
|
493
|
+
# Helper function: check if pre-commit output has REAL errors
|
|
494
|
+
has_real_errors() {
|
|
495
|
+
local log_file="$1"
|
|
291
496
|
|
|
292
|
-
|
|
293
|
-
|
|
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
|
|
297
|
-
|
|
298
|
-
|
|
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
|
-
|
|
301
|
-
|
|
302
|
-
|
|
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
|
-
|
|
313
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
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
|
-
|
|
552
|
+
((attempt++))
|
|
553
|
+
done
|
|
328
554
|
|
|
329
|
-
if
|
|
330
|
-
print_success "passed"
|
|
331
|
-
rm -f "$
|
|
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 "
|
|
337
|
-
|
|
338
|
-
|
|
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
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"test":
|
|
44
|
-
"
|
|
40
|
+
"lint": true,
|
|
41
|
+
"typecheck": true,
|
|
42
|
+
"build": "final",
|
|
43
|
+
"test": true,
|
|
44
|
+
"fastapi": true
|
|
45
45
|
},
|
|
46
46
|
|
|
47
47
|
"api": {
|
package/templates/config/go.json
CHANGED