thrivekit 2.0.18 → 2.0.20

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
@@ -4,7 +4,7 @@
4
4
 
5
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.
6
6
 
7
- > **Optimized for:** Python, TypeScript, React, and Docker projects. Auto-detects ports from `docker-compose.yml`, Vite, Next.js, and FastAPI.
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
8
 
9
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
10
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thrivekit",
3
- "version": "2.0.18",
3
+ "version": "2.0.20",
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,66 +186,70 @@ 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 web_port=""
191
192
 
192
- # Check docker-compose.yml for frontend/web service port mappings
193
+ # Priority 1: docker-compose.yml - parse actual port mappings
193
194
  for compose_file in "docker-compose.yml" "docker-compose.yaml" "compose.yml" "compose.yaml"; do
194
- if [[ -f "$compose_file" && -z "$base_url" ]]; then
195
- # Look for web/frontend service port mapping
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
- fi
203
200
  fi
204
201
  done
205
202
 
206
- # Check for Vite config (uses port 5173 by default)
207
- if [[ -z "$base_url" ]]; then
203
+ # Priority 2: Vite config - parse server.port
204
+ if [[ -z "$web_port" ]]; then
208
205
  for vite_config in "vite.config.ts" "vite.config.js" "apps/web/vite.config.ts" "apps/web/vite.config.js" \
209
206
  "apps/frontend/vite.config.ts" "frontend/vite.config.ts"; do
210
207
  if [[ -f "$vite_config" ]]; then
211
- base_url="http://localhost:5173"
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
211
+ break
212
+ fi
213
+ done
214
+ fi
215
+
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
212
222
  break
213
223
  fi
214
224
  done
215
225
  fi
216
226
 
217
- # Check for Next.js (uses port 3000 by default)
218
- if [[ -z "$base_url" ]]; then
227
+ # Priority 4: Next.js - check package.json dev script for -p flag
228
+ if [[ -z "$web_port" ]]; then
219
229
  for next_config in "next.config.js" "next.config.ts" "next.config.mjs" \
220
230
  "apps/web/next.config.js" "apps/web/next.config.mjs"; do
221
231
  if [[ -f "$next_config" ]]; then
222
- base_url="http://localhost:3000"
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
223
239
  break
224
240
  fi
225
241
  done
226
242
  fi
227
243
 
228
- # Fallback: Check package.json scripts for dev server port
229
- if [[ -z "$base_url" && -f "package.json" ]]; then
230
- if grep -q '"dev".*:3000' package.json 2>/dev/null; then
231
- base_url="http://localhost:3000"
232
- elif grep -q '"dev".*:5173' package.json 2>/dev/null; then
233
- base_url="http://localhost:5173"
234
- elif grep -q '"dev".*:8080' package.json 2>/dev/null; then
235
- base_url="http://localhost:8080"
236
- 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)
237
248
  fi
238
249
 
239
- # Check for monorepo frontend
240
- for fe_pkg in "apps/web/package.json" "apps/frontend/package.json" "frontend/package.json"; do
241
- if [[ -f "$fe_pkg" && -z "$base_url" ]]; then
242
- if grep -q ':3000' "$fe_pkg" 2>/dev/null; then
243
- base_url="http://localhost:3000"
244
- elif grep -q ':5173' "$fe_pkg" 2>/dev/null; then
245
- base_url="http://localhost:5173"
246
- fi
247
- fi
248
- done
250
+ if [[ -n "$web_port" ]]; then
251
+ base_url="http://localhost:$web_port"
252
+ fi
249
253
 
250
254
  if [[ -n "$base_url" ]]; then
251
255
  if jq -e '.testUrlBase' "$tmpfile" >/dev/null 2>&1 && [[ "$(jq -r '.testUrlBase' "$tmpfile")" != "" ]]; then
@@ -292,47 +296,52 @@ auto_configure_project() {
292
296
  fi
293
297
  fi
294
298
 
295
- # 4. Detect API baseUrl based on backend type
299
+ # 4. Detect API baseUrl by parsing actual config files for port values
296
300
  local api_url=""
301
+ local api_port=""
297
302
 
298
- # Check docker-compose.yml for API service port mappings (most common)
303
+ # Priority 1: docker-compose.yml - parse actual port mappings
299
304
  for compose_file in "docker-compose.yml" "docker-compose.yaml" "compose.yml" "compose.yaml"; do
300
- if [[ -f "$compose_file" && -z "$api_url" ]]; then
301
- # Look for api/backend service port mapping like "8000:8000" or "3001:3000"
302
- local api_port
305
+ if [[ -f "$compose_file" && -z "$api_port" ]]; then
306
+ # Extract port from api/backend service: "8000:8000" -> 8000
303
307
  api_port=$(grep -A 20 -E '^\s*(api|backend|server|app):' "$compose_file" 2>/dev/null | \
304
308
  grep -E '^\s*-\s*"?[0-9]+:[0-9]+"?' | head -1 | \
305
309
  grep -oE '[0-9]+:' | head -1 | tr -d ':')
306
- if [[ -n "$api_port" ]]; then
307
- api_url="http://localhost:$api_port"
308
- fi
309
310
  fi
310
311
  done
311
312
 
312
- if [[ -n "$backend_dir" && -z "$api_url" ]]; then
313
- # Check Dockerfile for EXPOSE directive
313
+ # Priority 2: Dockerfile EXPOSE directive
314
+ if [[ -n "$backend_dir" && -z "$api_port" ]]; then
314
315
  if [[ -f "$backend_dir/Dockerfile" ]]; then
315
- local docker_port
316
- docker_port=$(grep -i "^EXPOSE" "$backend_dir/Dockerfile" 2>/dev/null | head -1 | grep -oE '[0-9]+' | head -1)
317
- if [[ -n "$docker_port" ]]; then
318
- api_url="http://localhost:$docker_port"
319
- fi
316
+ api_port=$(grep -i "^EXPOSE" "$backend_dir/Dockerfile" 2>/dev/null | head -1 | grep -oE '[0-9]+' | head -1)
320
317
  fi
321
- # Python backends (FastAPI/Django) typically use port 8000
322
- if [[ -z "$api_url" ]] && { [[ -f "$backend_dir/pyproject.toml" ]] || [[ -f "$backend_dir/requirements.txt" ]] || [[ -f "$backend_dir/main.py" ]]; }; then
323
- api_url="http://localhost:8000"
324
- # Node backends - check for port in package.json or default to 3001
325
- elif [[ -z "$api_url" && -f "$backend_dir/package.json" ]]; then
326
- if grep -q ':3001' "$backend_dir/package.json" 2>/dev/null; then
327
- api_url="http://localhost:3001"
328
- elif grep -q ':4000' "$backend_dir/package.json" 2>/dev/null; then
329
- api_url="http://localhost:4000"
330
- else
331
- api_url="http://localhost:3001"
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)
332
326
  fi
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"
333
333
  fi
334
334
  fi
335
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
+
336
345
  if [[ -n "$api_url" ]]; then
337
346
  if jq -e '.api.baseUrl' "$tmpfile" >/dev/null 2>&1 && [[ "$(jq -r '.api.baseUrl' "$tmpfile")" != "" ]]; then
338
347
  : # Already set
package/ralph/setup.sh CHANGED
@@ -257,6 +257,15 @@ setup_claude_md() {
257
257
  fi
258
258
  [[ -f "manage.py" ]] && framework="${framework:+$framework + }Django"
259
259
 
260
+ # Detect Hugo (Go static site generator)
261
+ for hugo_config in "hugo.toml" "hugo.yaml" "hugo.json" "config.toml"; do
262
+ if [[ -f "$hugo_config" ]] && [[ -d "content" || -d "layouts" || -d "themes" ]]; then
263
+ framework="${framework:+$framework + }Hugo"
264
+ [[ -z "$runtime" ]] && runtime="Go"
265
+ break
266
+ fi
267
+ done
268
+
260
269
  # Detect TypeScript
261
270
  [[ -f "tsconfig.json" || -f "${fe_dir}/tsconfig.json" ]] && language="TypeScript"
262
271