thrivekit 2.0.19 → 2.0.21

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.
@@ -163,7 +163,35 @@ Break the idea into small, executable PRDs following the JSON structure below.
163
163
 
164
164
  **STOP and wait for user response. Do not proceed until they approve.**
165
165
 
166
- ### Step 7: Final Instructions
166
+ ### Step 7: Final Review
167
+
168
+ Before finishing, do a quick sanity check on the PRD:
169
+
170
+ 1. **Read the PRD back:**
171
+ ```bash
172
+ cat .ralph/prd.json | jq '.'
173
+ ```
174
+
175
+ 2. **Review for common issues:**
176
+ - Are story IDs sequential and unique?
177
+ - Does each story have testable acceptance criteria?
178
+ - Are file paths specific (not vague like "src/")?
179
+ - Are dependencies correctly ordered?
180
+ - Are testSteps actually executable shell commands?
181
+ - Is any story too large (>4 acceptance criteria)?
182
+
183
+ 3. **If issues found**, say:
184
+ "I found some issues with the PRD:
185
+ - [issue 1]
186
+ - [issue 2]
187
+
188
+ Fixing now..."
189
+
190
+ Then fix the issues and rewrite the PRD.
191
+
192
+ 4. **If no issues**, proceed to final instructions.
193
+
194
+ ### Step 8: Final Instructions
167
195
 
168
196
  Once the user approves the PRD, say:
169
197
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thrivekit",
3
- "version": "2.0.19",
3
+ "version": "2.0.21",
4
4
  "description": "Tools to thrive with agentic coding - RALPH autonomous loop, Claude Code hooks, PRD-driven development",
5
5
  "author": "Allie Jones <allie@allthrive.ai>",
6
6
  "license": "MIT",
package/ralph/init.sh CHANGED
@@ -186,67 +186,69 @@ auto_configure_project() {
186
186
  fi
187
187
  fi
188
188
 
189
- # 2. Detect testUrlBase from common patterns
189
+ # 2. Detect testUrlBase by parsing actual config files for port values
190
190
  local base_url=""
191
- local detected_framework=""
191
+ local web_port=""
192
192
 
193
- # Check docker-compose.yml for frontend/web service port mappings (highest priority)
193
+ # Priority 1: docker-compose.yml - parse actual port mappings
194
194
  for compose_file in "docker-compose.yml" "docker-compose.yaml" "compose.yml" "compose.yaml"; do
195
- if [[ -f "$compose_file" && -z "$base_url" ]]; then
196
- local web_port
195
+ if [[ -f "$compose_file" && -z "$web_port" ]]; then
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
199
  grep -oE '[0-9]+:' | head -1 | tr -d ':')
200
- if [[ -n "$web_port" ]]; then
201
- base_url="http://localhost:$web_port"
202
- detected_framework="docker"
203
- fi
204
200
  fi
205
201
  done
206
202
 
207
- # Detect framework and use default port from constants
208
- if [[ -z "$base_url" ]]; then
209
- # Vite
203
+ # Priority 2: Vite config - parse server.port
204
+ if [[ -z "$web_port" ]]; then
210
205
  for vite_config in "vite.config.ts" "vite.config.js" "apps/web/vite.config.ts" "apps/web/vite.config.js" \
211
206
  "apps/frontend/vite.config.ts" "frontend/vite.config.ts"; do
212
207
  if [[ -f "$vite_config" ]]; then
213
- detected_framework="vite"
214
- base_url="http://localhost:${DEFAULT_PORTS[vite]}"
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)
210
+ [[ -z "$web_port" ]] && web_port="5173" # Vite's documented default
215
211
  break
216
212
  fi
217
213
  done
218
214
  fi
219
215
 
220
- if [[ -z "$base_url" ]]; then
221
- # Next.js
222
- for next_config in "next.config.js" "next.config.ts" "next.config.mjs" \
223
- "apps/web/next.config.js" "apps/web/next.config.mjs"; do
224
- if [[ -f "$next_config" ]]; then
225
- detected_framework="nextjs"
226
- base_url="http://localhost:${DEFAULT_PORTS[nextjs]}"
216
+ # Priority 3: Hugo config - parse server.port or use documented default
217
+ if [[ -z "$web_port" ]]; then
218
+ for hugo_config in "hugo.toml" "hugo.yaml" "hugo.json" "config.toml"; do
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)
221
+ [[ -z "$web_port" ]] && web_port="1313" # Hugo's documented default
227
222
  break
228
223
  fi
229
224
  done
230
225
  fi
231
226
 
232
- if [[ -z "$base_url" ]]; then
233
- # Hugo
234
- for hugo_config in "hugo.toml" "hugo.yaml" "hugo.json" "config.toml"; do
235
- if [[ -f "$hugo_config" ]] && [[ -d "content" || -d "layouts" || -d "themes" ]]; then
236
- detected_framework="hugo"
237
- base_url="http://localhost:${DEFAULT_PORTS[hugo]}"
227
+ # Priority 4: Next.js - check package.json dev script for -p flag
228
+ if [[ -z "$web_port" ]]; then
229
+ for next_config in "next.config.js" "next.config.ts" "next.config.mjs" \
230
+ "apps/web/next.config.js" "apps/web/next.config.mjs"; do
231
+ if [[ -f "$next_config" ]]; then
232
+ # Check if package.json has custom port: "dev": "next dev -p 4000"
233
+ local pkg_dir
234
+ pkg_dir=$(dirname "$next_config")
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]+')
237
+ fi
238
+ [[ -z "$web_port" ]] && web_port="3000" # Next.js documented default
238
239
  break
239
240
  fi
240
241
  done
241
242
  fi
242
243
 
243
- # Fallback: Check package.json for port hints
244
- if [[ -z "$base_url" && -f "package.json" ]]; then
245
- local pkg_port
246
- pkg_port=$(grep -oE ':[0-9]{4}' package.json 2>/dev/null | head -1 | tr -d ':')
247
- if [[ -n "$pkg_port" ]]; then
248
- base_url="http://localhost:$pkg_port"
249
- fi
244
+ # Priority 5: Generic package.json port detection
245
+ if [[ -z "$web_port" && -f "package.json" ]]; then
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)
248
+ fi
249
+
250
+ if [[ -n "$web_port" ]]; then
251
+ base_url="http://localhost:$web_port"
250
252
  fi
251
253
 
252
254
  if [[ -n "$base_url" ]]; then
@@ -294,55 +296,52 @@ auto_configure_project() {
294
296
  fi
295
297
  fi
296
298
 
297
- # 4. Detect API baseUrl based on backend type
299
+ # 4. Detect API baseUrl by parsing actual config files for port values
298
300
  local api_url=""
299
- local api_framework=""
301
+ local api_port=""
300
302
 
301
- # Check docker-compose.yml for API service port mappings (highest priority)
303
+ # Priority 1: docker-compose.yml - parse actual port mappings
302
304
  for compose_file in "docker-compose.yml" "docker-compose.yaml" "compose.yml" "compose.yaml"; do
303
- if [[ -f "$compose_file" && -z "$api_url" ]]; then
304
- local api_port
305
+ if [[ -f "$compose_file" && -z "$api_port" ]]; then
306
+ # Extract port from api/backend service: "8000:8000" -> 8000
305
307
  api_port=$(grep -A 20 -E '^\s*(api|backend|server|app):' "$compose_file" 2>/dev/null | \
306
308
  grep -E '^\s*-\s*"?[0-9]+:[0-9]+"?' | head -1 | \
307
309
  grep -oE '[0-9]+:' | head -1 | tr -d ':')
308
- if [[ -n "$api_port" ]]; then
309
- api_url="http://localhost:$api_port"
310
- api_framework="docker"
311
- fi
312
310
  fi
313
311
  done
314
312
 
315
- if [[ -n "$backend_dir" && -z "$api_url" ]]; then
316
- # Check Dockerfile for EXPOSE directive
313
+ # Priority 2: Dockerfile EXPOSE directive
314
+ if [[ -n "$backend_dir" && -z "$api_port" ]]; then
317
315
  if [[ -f "$backend_dir/Dockerfile" ]]; then
318
- local docker_port
319
- docker_port=$(grep -i "^EXPOSE" "$backend_dir/Dockerfile" 2>/dev/null | head -1 | grep -oE '[0-9]+' | head -1)
320
- if [[ -n "$docker_port" ]]; then
321
- api_url="http://localhost:$docker_port"
322
- api_framework="docker"
323
- fi
316
+ api_port=$(grep -i "^EXPOSE" "$backend_dir/Dockerfile" 2>/dev/null | head -1 | grep -oE '[0-9]+' | head -1)
324
317
  fi
325
- # Python backends - detect framework and use default port
326
- if [[ -z "$api_url" ]] && { [[ -f "$backend_dir/pyproject.toml" ]] || [[ -f "$backend_dir/requirements.txt" ]] || [[ -f "$backend_dir/main.py" ]]; }; then
327
- if grep -q "fastapi\|FastAPI" "$backend_dir"/*.py "$backend_dir/pyproject.toml" 2>/dev/null; then
328
- api_framework="fastapi"
329
- elif grep -q "django\|Django" "$backend_dir"/*.py "$backend_dir/pyproject.toml" 2>/dev/null; then
330
- api_framework="django"
331
- elif grep -q "flask\|Flask" "$backend_dir"/*.py "$backend_dir/pyproject.toml" 2>/dev/null; then
332
- api_framework="flask"
333
- else
334
- api_framework="fastapi" # Default for Python
318
+ fi
319
+
320
+ # Priority 3: Python - check for uvicorn/gunicorn port in scripts or pyproject.toml
321
+ if [[ -n "$backend_dir" && -z "$api_port" ]]; then
322
+ if [[ -f "$backend_dir/pyproject.toml" ]] || [[ -f "$backend_dir/requirements.txt" ]]; then
323
+ # Check pyproject.toml for port config
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)
335
326
  fi
336
- api_url="http://localhost:${DEFAULT_PORTS[$api_framework]}"
337
- # Node backends - check for port in package.json
338
- elif [[ -z "$api_url" && -f "$backend_dir/package.json" ]]; then
339
- local node_port
340
- node_port=$(grep -oE ':[0-9]{4}' "$backend_dir/package.json" 2>/dev/null | head -1 | tr -d ':')
341
- api_url="http://localhost:${node_port:-${DEFAULT_PORTS[express]}}"
342
- api_framework="express"
327
+ # Check for uvicorn command with --port
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)
330
+ fi
331
+ # Uvicorn/FastAPI default
332
+ [[ -z "$api_port" ]] && api_port="8000"
343
333
  fi
344
334
  fi
345
335
 
336
+ # Priority 4: Node backend - check package.json for port
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)
339
+ fi
340
+
341
+ if [[ -n "$api_port" ]]; then
342
+ api_url="http://localhost:$api_port"
343
+ fi
344
+
346
345
  if [[ -n "$api_url" ]]; then
347
346
  if jq -e '.api.baseUrl' "$tmpfile" >/dev/null 2>&1 && [[ "$(jq -r '.api.baseUrl' "$tmpfile")" != "" ]]; then
348
347
  : # Already set
package/ralph/utils.sh CHANGED
@@ -24,20 +24,6 @@ readonly CURL_TIMEOUT_SECONDS=10
24
24
  readonly FRONTEND_DIRS=("apps/web" "frontend" "client" "web")
25
25
  readonly BACKEND_DIRS=("apps/api" "api" "backend" "server")
26
26
 
27
- # Default ports by framework (used only during detection, then stored in config)
28
- declare -A DEFAULT_PORTS=(
29
- ["vite"]=5173
30
- ["nextjs"]=3000
31
- ["hugo"]=1313
32
- ["react"]=3000
33
- ["vue"]=5173
34
- ["express"]=3001
35
- ["fastapi"]=8000
36
- ["django"]=8000
37
- ["flask"]=5000
38
- ["rails"]=3000
39
- )
40
-
41
27
  # Track temp files for safe cleanup
42
28
  RALPH_TEMP_FILES=()
43
29