agentic-loop 3.5.1 → 3.6.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
@@ -4,7 +4,25 @@
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.
7
+ > **Optimized for:** Python, TypeScript, React, Go/Hugo, FastMCP, and Docker projects.
8
+
9
+ ---
10
+
11
+ ## Supported Project Types
12
+
13
+ Ralph auto-detects your project type and configures itself accordingly:
14
+
15
+ | Type | Detection | Auto-Configured |
16
+ |------|-----------|-----------------|
17
+ | **FastMCP** | `fastmcp` in pyproject.toml | Server module, MCP port, transport, subprojects |
18
+ | **FastAPI** | `fastapi` in pyproject.toml | uvicorn dev server, pytest, ruff |
19
+ | **Django** | `django` in pyproject.toml or manage.py | migrations, pytest, ruff |
20
+ | **Python** | pyproject.toml or requirements.txt | pytest, ruff, uv/poetry detection |
21
+ | **Node.js** | package.json | npm/yarn/pnpm, vitest/jest, eslint |
22
+ | **React** | `react` in package.json | Vite/Next.js, TypeScript, Tailwind |
23
+ | **Go/Hugo** | go.mod or hugo.toml | Hugo server, Go build |
24
+ | **Rust** | Cargo.toml | cargo build/test/clippy |
25
+ | **Fullstack** | frontend + backend directories | Monorepo support, separate configs |
8
26
 
9
27
  ---
10
28
 
package/bin/ralph.sh CHANGED
@@ -37,10 +37,44 @@ export RALPH_DIR PROMPT_FILE RALPH_LIB RALPH_TEMPLATES
37
37
  if [[ ! -d ".ralph" ]]; then
38
38
  mkdir -p ".ralph/archive" ".ralph/screenshots"
39
39
  fi
40
- # Always ensure config.json exists and run auto-detection if newly created
40
+ # Check if config.json needs to be created (but don't create it yet - let init copy the template)
41
41
  if [[ ! -f ".ralph/config.json" ]]; then
42
- echo '{}' > ".ralph/config.json"
43
- # Run auto-detection (function is in init.sh, sourced below)
42
+ # Copy config template based on detected project type
43
+ _project_type="minimal"
44
+ if [[ -f "pyproject.toml" ]]; then
45
+ if grep -qE "(fastmcp|\"fastmcp\"|'fastmcp')" pyproject.toml 2>/dev/null; then
46
+ _project_type="fastmcp"
47
+ elif grep -qE "(fastapi|\"fastapi\"|'fastapi')" pyproject.toml 2>/dev/null; then
48
+ _project_type="fastapi"
49
+ elif grep -qE "(django|\"django\"|'django')" pyproject.toml 2>/dev/null || [[ -f "manage.py" ]]; then
50
+ _project_type="django"
51
+ else
52
+ _project_type="python"
53
+ fi
54
+ elif [[ -f "go.mod" ]]; then
55
+ _project_type="go"
56
+ elif [[ -f "Cargo.toml" ]]; then
57
+ _project_type="rust"
58
+ elif [[ -d "frontend" ]] && [[ -d "backend" || -d "core" || -d "apps" ]]; then
59
+ _project_type="fullstack"
60
+ elif [[ -f "package.json" ]]; then
61
+ _project_type="node"
62
+ fi
63
+
64
+ _config_template="$RALPH_TEMPLATES/config/${_project_type}.json"
65
+ if [[ -f "$_config_template" ]]; then
66
+ cp "$_config_template" ".ralph/config.json"
67
+ else
68
+ # Fall back to minimal config if template doesn't exist
69
+ _config_template="$RALPH_TEMPLATES/config/minimal.json"
70
+ if [[ -f "$_config_template" ]]; then
71
+ cp "$_config_template" ".ralph/config.json"
72
+ else
73
+ echo '{"projectType": "unknown"}' > ".ralph/config.json"
74
+ fi
75
+ fi
76
+ unset _project_type _config_template
77
+ # Run auto-detection to fill in project-specific values
44
78
  _ralph_needs_autoconfig=true
45
79
  fi
46
80
  [[ ! -f ".ralph/signs.json" ]] && echo '{"signs": []}' > ".ralph/signs.json"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-loop",
3
- "version": "3.5.1",
3
+ "version": "3.6.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
@@ -145,8 +145,35 @@ detect_project_type() {
145
145
  project_type="rust"
146
146
  elif [[ -f "go.mod" ]]; then
147
147
  project_type="go"
148
- elif [[ -f "pyproject.toml" || -f "requirements.txt" || -f "setup.py" ]]; then
149
- project_type="python"
148
+ # Check for Python framework variants (more specific first)
149
+ elif [[ -f "pyproject.toml" ]]; then
150
+ # FastMCP detection (check for fastmcp in any quote style)
151
+ if grep -qiE "(fastmcp|\"fastmcp\"|'fastmcp')" pyproject.toml 2>/dev/null; then
152
+ project_type="fastmcp"
153
+ # Django detection
154
+ elif grep -qiE "(django|\"django\"|'django')" pyproject.toml 2>/dev/null || [[ -f "manage.py" ]]; then
155
+ project_type="django"
156
+ # FastAPI detection
157
+ elif grep -qiE "(fastapi|\"fastapi\"|'fastapi')" pyproject.toml 2>/dev/null; then
158
+ project_type="fastapi"
159
+ else
160
+ project_type="python"
161
+ fi
162
+ elif [[ -f "requirements.txt" || -f "setup.py" ]]; then
163
+ # Check requirements.txt for frameworks
164
+ if [[ -f "requirements.txt" ]]; then
165
+ if grep -qi 'fastmcp' requirements.txt 2>/dev/null; then
166
+ project_type="fastmcp"
167
+ elif grep -qi 'django' requirements.txt 2>/dev/null || [[ -f "manage.py" ]]; then
168
+ project_type="django"
169
+ elif grep -qi 'fastapi' requirements.txt 2>/dev/null; then
170
+ project_type="fastapi"
171
+ else
172
+ project_type="python"
173
+ fi
174
+ else
175
+ project_type="python"
176
+ fi
150
177
  elif [[ -f "package.json" ]]; then
151
178
  project_type="node"
152
179
  fi
@@ -372,6 +399,101 @@ auto_configure_project() {
372
399
  fi
373
400
  fi
374
401
 
402
+ # 6. FastMCP-specific detection
403
+ if [[ -f "pyproject.toml" ]] && grep -qiE "(fastmcp|\"fastmcp\"|'fastmcp')" pyproject.toml 2>/dev/null; then
404
+ # Detect MCP server module from entry points
405
+ local mcp_module=""
406
+ # Look for [project.scripts] section with pattern: name = "module.server:main"
407
+ mcp_module=$(grep -A 20 '^\[project\.scripts\]' pyproject.toml 2>/dev/null | \
408
+ grep -E '^\w+\s*=\s*"[^"]+\.server:' | head -1 | \
409
+ sed -E 's/.*"([^"]+)\.server:.*/\1/' || true)
410
+
411
+ # Fallback: detect from src directory structure
412
+ if [[ -z "$mcp_module" && -d "src" ]]; then
413
+ for dir in src/*/; do
414
+ if [[ -f "${dir}server.py" ]]; then
415
+ mcp_module=$(basename "${dir%/}")
416
+ break
417
+ fi
418
+ done
419
+ fi
420
+
421
+ if [[ -n "$mcp_module" ]]; then
422
+ if ! jq -e '.mcp.serverModule' "$tmpfile" >/dev/null 2>&1 || [[ "$(jq -r '.mcp.serverModule' "$tmpfile")" == "" ]]; then
423
+ jq --arg mod "$mcp_module" '.mcp.serverModule = $mod' "$tmpfile" > "${tmpfile}.new" && mv "${tmpfile}.new" "$tmpfile"
424
+ # Also update the dev command
425
+ jq --arg cmd "python -m ${mcp_module}.server" '.commands.dev = $cmd' "$tmpfile" > "${tmpfile}.new" && mv "${tmpfile}.new" "$tmpfile"
426
+ echo " Auto-detected mcp.serverModule: $mcp_module"
427
+ updated=true
428
+ fi
429
+ fi
430
+
431
+ # Detect MCP port from .env or docker-compose
432
+ local mcp_port=""
433
+ if [[ -f ".env" ]]; then
434
+ mcp_port=$(grep -E '^[A-Z_]*PORT=' .env 2>/dev/null | grep -v '#' | head -1 | grep -oE '[0-9]+' || true)
435
+ fi
436
+ if [[ -z "$mcp_port" ]]; then
437
+ for compose_file in "docker-compose.yml" "docker-compose.yaml"; do
438
+ if [[ -f "$compose_file" ]]; then
439
+ # Look for port in main app service
440
+ mcp_port=$(grep -A 20 -E '^\s*(app|gopa|mcp|server):' "$compose_file" 2>/dev/null | \
441
+ grep -E '^\s*-\s*"?[0-9]+:[0-9]+"?' | head -1 | \
442
+ grep -oE '[0-9]+:' | head -1 | tr -d ':' || true)
443
+ [[ -n "$mcp_port" ]] && break
444
+ fi
445
+ done
446
+ fi
447
+
448
+ if [[ -n "$mcp_port" ]]; then
449
+ if ! jq -e '.api.baseUrl' "$tmpfile" >/dev/null 2>&1 || [[ "$(jq -r '.api.baseUrl' "$tmpfile")" == "http://localhost:8000" ]]; then
450
+ jq --arg url "http://localhost:$mcp_port" '.api.baseUrl = $url | .urls.app = $url' "$tmpfile" > "${tmpfile}.new" && mv "${tmpfile}.new" "$tmpfile"
451
+ echo " Auto-detected MCP port: $mcp_port"
452
+ updated=true
453
+ fi
454
+ fi
455
+
456
+ # Detect MCP transport from .env
457
+ if [[ -f ".env" ]]; then
458
+ local transport=""
459
+ transport=$(grep -E '^[A-Z_]*TRANSPORT=' .env 2>/dev/null | grep -v '#' | head -1 | cut -d'=' -f2 | tr -d '"'"'" || true)
460
+ if [[ -n "$transport" ]]; then
461
+ jq --arg t "$transport" '.mcp.transport = $t' "$tmpfile" > "${tmpfile}.new" && mv "${tmpfile}.new" "$tmpfile"
462
+ fi
463
+ fi
464
+
465
+ # Detect subprojects (directories with their own package.json)
466
+ for subdir in */; do
467
+ local subdir_name="${subdir%/}"
468
+ if [[ -f "${subdir}package.json" && "$subdir_name" != "node_modules" ]]; then
469
+ # Check if subproject already configured
470
+ if ! jq -e ".subprojects[\"$subdir_name\"]" "$tmpfile" >/dev/null 2>&1; then
471
+ local sub_lint="" sub_build="" sub_dev=""
472
+ # Detect scripts from package.json
473
+ if grep -q '"lint"' "${subdir}package.json" 2>/dev/null; then
474
+ sub_lint="npm run lint"
475
+ fi
476
+ if grep -q '"build"' "${subdir}package.json" 2>/dev/null; then
477
+ sub_build="npm run build"
478
+ fi
479
+ if grep -q '"dev"' "${subdir}package.json" 2>/dev/null; then
480
+ sub_dev="npm run dev"
481
+ fi
482
+
483
+ jq --arg name "$subdir_name" \
484
+ --arg path "$subdir_name" \
485
+ --arg lint "$sub_lint" \
486
+ --arg build "$sub_build" \
487
+ --arg dev "$sub_dev" \
488
+ '.subprojects[$name] = {path: $path, commands: {lint: $lint, build: $build, dev: $dev}}' \
489
+ "$tmpfile" > "${tmpfile}.new" && mv "${tmpfile}.new" "$tmpfile"
490
+ echo " Auto-detected subproject: $subdir_name"
491
+ updated=true
492
+ fi
493
+ fi
494
+ done
495
+ fi
496
+
375
497
  # Save if updated
376
498
  if [[ "$updated" == "true" ]]; then
377
499
  mv "$tmpfile" "$config"
package/ralph/setup.sh CHANGED
@@ -129,35 +129,74 @@ setup_ralph_dir() {
129
129
  # Copy config template based on detected project type
130
130
  if [[ ! -f ".ralph/config.json" ]]; then
131
131
  local config_template=""
132
+ local detected_type=""
132
133
  # Check for Go projects
133
134
  if [[ -f "go.mod" ]]; then
134
135
  config_template="$pkg_root/templates/config/go.json"
136
+ detected_type="go"
135
137
  # Check for Rust projects
136
138
  elif [[ -f "Cargo.toml" ]]; then
137
139
  config_template="$pkg_root/templates/config/rust.json"
140
+ detected_type="rust"
138
141
  # Check for Hugo projects
139
142
  elif [[ -f "hugo.toml" || -f "hugo.yaml" || -f "config.toml" ]] && [[ -d "content" || -d "layouts" || -d "themes" ]]; then
140
143
  config_template="$pkg_root/templates/config/go.json"
141
- # Check for Python projects
142
- elif [[ -f "manage.py" ]] || [[ -f "pyproject.toml" ]]; then
144
+ detected_type="hugo"
145
+ # Check for Python framework variants (more specific first)
146
+ elif [[ -f "pyproject.toml" ]]; then
147
+ if grep -qiE "(fastmcp|\"fastmcp\"|'fastmcp')" pyproject.toml 2>/dev/null; then
148
+ config_template="$pkg_root/templates/config/fastmcp.json"
149
+ detected_type="fastmcp"
150
+ elif grep -qiE "(fastapi|\"fastapi\"|'fastapi')" pyproject.toml 2>/dev/null; then
151
+ config_template="$pkg_root/templates/config/python.json"
152
+ detected_type="fastapi"
153
+ elif grep -qiE "(django|\"django\"|'django')" pyproject.toml 2>/dev/null || [[ -f "manage.py" ]]; then
154
+ config_template="$pkg_root/templates/config/python.json"
155
+ detected_type="django"
156
+ else
157
+ config_template="$pkg_root/templates/config/python.json"
158
+ detected_type="python"
159
+ fi
160
+ elif [[ -f "requirements.txt" ]]; then
161
+ if grep -qi 'fastmcp' requirements.txt 2>/dev/null; then
162
+ config_template="$pkg_root/templates/config/fastmcp.json"
163
+ detected_type="fastmcp"
164
+ elif grep -qi 'fastapi' requirements.txt 2>/dev/null; then
165
+ config_template="$pkg_root/templates/config/python.json"
166
+ detected_type="fastapi"
167
+ elif grep -qi 'django' requirements.txt 2>/dev/null || [[ -f "manage.py" ]]; then
168
+ config_template="$pkg_root/templates/config/python.json"
169
+ detected_type="django"
170
+ else
171
+ config_template="$pkg_root/templates/config/python.json"
172
+ detected_type="python"
173
+ fi
174
+ elif [[ -f "manage.py" ]]; then
143
175
  config_template="$pkg_root/templates/config/python.json"
176
+ detected_type="django"
144
177
  # Check for fullstack projects
145
178
  elif [[ -d "frontend" ]] && [[ -d "backend" || -d "core" || -d "apps" ]]; then
146
179
  config_template="$pkg_root/templates/config/fullstack.json"
180
+ detected_type="fullstack"
147
181
  # Check for Node projects
148
182
  elif [[ -f "package.json" ]]; then
149
183
  config_template="$pkg_root/templates/config/node.json"
184
+ detected_type="node"
150
185
  fi
151
186
 
152
187
  if [[ -n "$config_template" ]] && [[ -f "$config_template" ]]; then
153
188
  cp "$config_template" ".ralph/config.json"
189
+ echo " Created .ralph/config.json (detected: $detected_type)"
154
190
  else
155
191
  echo '{"checks": {"build": true, "lint": true, "test": true}}' > ".ralph/config.json"
192
+ echo " Created .ralph/config.json"
156
193
  fi
157
- echo " Created .ralph/config.json"
194
+
195
+ # Store detected type for CLAUDE.md generation
196
+ export RALPH_DETECTED_TYPE="$detected_type"
158
197
  fi
159
198
 
160
- # Copy signs template
199
+ # Copy or merge signs template
161
200
  if [[ ! -f ".ralph/signs.json" ]]; then
162
201
  if [[ -f "$pkg_root/templates/signs.json" ]]; then
163
202
  cp "$pkg_root/templates/signs.json" ".ralph/signs.json"
@@ -165,6 +204,30 @@ setup_ralph_dir() {
165
204
  echo '{"signs": []}' > ".ralph/signs.json"
166
205
  fi
167
206
  echo " Created .ralph/signs.json"
207
+ else
208
+ # Merge new default signs into existing file
209
+ if [[ -f "$pkg_root/templates/signs.json" ]] && command -v jq &>/dev/null; then
210
+ local existing_ids new_signs_added=0
211
+ existing_ids=$(jq -r '.signs[].id // empty' ".ralph/signs.json" 2>/dev/null | tr '\n' '|')
212
+
213
+ # Add signs from template that don't exist locally
214
+ while IFS= read -r sign; do
215
+ local sign_id
216
+ sign_id=$(echo "$sign" | jq -r '.id // empty')
217
+ if [[ -n "$sign_id" && ! "$existing_ids" =~ "$sign_id" ]]; then
218
+ # Add this sign to local file
219
+ local tmp_file
220
+ tmp_file=$(mktemp)
221
+ jq --argjson new_sign "$sign" '.signs += [$new_sign]' ".ralph/signs.json" > "$tmp_file"
222
+ mv "$tmp_file" ".ralph/signs.json"
223
+ ((new_signs_added++))
224
+ fi
225
+ done < <(jq -c '.signs[]' "$pkg_root/templates/signs.json" 2>/dev/null)
226
+
227
+ if [[ $new_signs_added -gt 0 ]]; then
228
+ echo " Merged $new_signs_added new sign(s) into .ralph/signs.json"
229
+ fi
230
+ fi
168
231
  fi
169
232
 
170
233
  # Create or update PROMPT.md
@@ -323,6 +386,8 @@ setup_slash_commands() {
323
386
  # Generate CLAUDE.md with detected project info
324
387
  setup_claude_md() {
325
388
  local marker="<!-- agentic-loop-detected -->"
389
+ local pkg_root
390
+ pkg_root="$(cd "$RALPH_LIB/.." && pwd)"
326
391
 
327
392
  # Skip if we already added our section
328
393
  [[ -f "CLAUDE.md" ]] && grep -q "$marker" "CLAUDE.md" 2>/dev/null && return 0
@@ -330,6 +395,7 @@ setup_claude_md() {
330
395
  echo "Generating CLAUDE.md..."
331
396
 
332
397
  local runtime="" framework="" language="" styling="" testing="" structure=""
398
+ local framework_type="" # For template selection
333
399
 
334
400
  # Detect runtime/language
335
401
  local fe_dir=""
@@ -344,18 +410,45 @@ setup_claude_md() {
344
410
  [[ -f "pyproject.toml" || -f "requirements.txt" || -f "manage.py" ]] && runtime="${runtime:+$runtime + }Python"
345
411
  [[ -f "Gemfile" ]] && runtime="Ruby"
346
412
 
347
- # Detect framework
413
+ # Detect framework (with template type detection)
348
414
  local pkg="package.json"
349
415
  [[ -n "$fe_dir" && -f "${fe_dir}/package.json" ]] && pkg="${fe_dir}/package.json"
350
416
 
351
417
  if [[ -f "$pkg" ]]; then
352
418
  grep -q '"next"' "$pkg" 2>/dev/null && framework="Next.js"
353
- grep -q '"react"' "$pkg" 2>/dev/null && [[ -z "$framework" ]] && framework="React"
419
+ grep -q '"react"' "$pkg" 2>/dev/null && [[ -z "$framework" ]] && framework="React" && framework_type="react"
354
420
  grep -q '"vue"' "$pkg" 2>/dev/null && framework="Vue"
355
421
  grep -q '"svelte"' "$pkg" 2>/dev/null && framework="Svelte"
356
422
  grep -q '"express"' "$pkg" 2>/dev/null && framework="${framework:+$framework + }Express"
357
423
  fi
358
- [[ -f "manage.py" ]] && framework="${framework:+$framework + }Django"
424
+
425
+ # Detect Python frameworks (more specific detection)
426
+ if [[ -f "pyproject.toml" ]]; then
427
+ if grep -qiE "(fastmcp|\"fastmcp\"|'fastmcp')" pyproject.toml 2>/dev/null; then
428
+ framework="${framework:+$framework + }FastMCP"
429
+ framework_type="fastmcp"
430
+ elif grep -qiE "(fastapi|\"fastapi\"|'fastapi')" pyproject.toml 2>/dev/null; then
431
+ framework="${framework:+$framework + }FastAPI"
432
+ framework_type="fastapi"
433
+ elif grep -qiE "(django|\"django\"|'django')" pyproject.toml 2>/dev/null || [[ -f "manage.py" ]]; then
434
+ framework="${framework:+$framework + }Django"
435
+ framework_type="django"
436
+ fi
437
+ elif [[ -f "requirements.txt" ]]; then
438
+ if grep -qi 'fastmcp' requirements.txt 2>/dev/null; then
439
+ framework="${framework:+$framework + }FastMCP"
440
+ framework_type="fastmcp"
441
+ elif grep -qi 'fastapi' requirements.txt 2>/dev/null; then
442
+ framework="${framework:+$framework + }FastAPI"
443
+ framework_type="fastapi"
444
+ elif grep -qi 'django' requirements.txt 2>/dev/null || [[ -f "manage.py" ]]; then
445
+ framework="${framework:+$framework + }Django"
446
+ framework_type="django"
447
+ fi
448
+ elif [[ -f "manage.py" ]]; then
449
+ framework="${framework:+$framework + }Django"
450
+ framework_type="django"
451
+ fi
359
452
 
360
453
  # Detect Hugo (Go static site generator)
361
454
  for hugo_config in "hugo.toml" "hugo.yaml" "hugo.json" "config.toml"; do
@@ -376,6 +469,7 @@ setup_claude_md() {
376
469
  [[ -f "vitest.config.ts" || -f "vitest.config.js" ]] && testing="Vitest"
377
470
  [[ -f "jest.config.js" || -f "jest.config.ts" ]] && testing="Jest"
378
471
  [[ -f "playwright.config.ts" || -f "playwright.config.js" ]] && testing="${testing:+$testing + }Playwright"
472
+ [[ -f "pytest.ini" || -f "pyproject.toml" ]] && grep -q 'pytest' pyproject.toml 2>/dev/null && testing="${testing:+$testing + }pytest"
379
473
 
380
474
  # Detect Python package manager
381
475
  local python_runner=""
@@ -404,17 +498,46 @@ ${python_runner:+- Python: Use \`$python_runner\` (not bare \`python\`)}
404
498
 
405
499
  *Auto-detected by agentic-loop. Edit freely.*"
406
500
 
501
+ # Check for framework-specific template
502
+ local framework_template=""
503
+ if [[ -n "$framework_type" ]]; then
504
+ framework_template="$pkg_root/templates/examples/CLAUDE-${framework_type}.md"
505
+ fi
506
+
407
507
  if [[ -f "CLAUDE.md" ]]; then
508
+ # Append framework template if it exists and not already included
509
+ if [[ -n "$framework_template" && -f "$framework_template" ]]; then
510
+ local template_marker="<!-- CLAUDE-${framework_type} -->"
511
+ if ! grep -q "$template_marker" "CLAUDE.md" 2>/dev/null; then
512
+ echo "" >> CLAUDE.md
513
+ echo "$template_marker" >> CLAUDE.md
514
+ # Skip the first line (# CLAUDE.md - ...) of the template
515
+ tail -n +2 "$framework_template" >> CLAUDE.md
516
+ echo " Appended ${framework_type} conventions to CLAUDE.md"
517
+ fi
518
+ fi
408
519
  echo "$detected_section" >> CLAUDE.md
409
520
  echo " Updated CLAUDE.md"
410
521
  else
522
+ # Create new CLAUDE.md
411
523
  cat > CLAUDE.md << EOF
412
524
  # Project Guide for Claude
413
525
 
414
526
  ## Your Rules
415
527
  <!-- Add your project-specific rules, patterns, and conventions here -->
416
- $detected_section
417
528
  EOF
529
+
530
+ # Include framework template if available
531
+ if [[ -n "$framework_template" && -f "$framework_template" ]]; then
532
+ local template_marker="<!-- CLAUDE-${framework_type} -->"
533
+ echo "" >> CLAUDE.md
534
+ echo "$template_marker" >> CLAUDE.md
535
+ # Skip the first line (# CLAUDE.md - ...) of the template
536
+ tail -n +2 "$framework_template" >> CLAUDE.md
537
+ echo " Included ${framework_type} conventions"
538
+ fi
539
+
540
+ echo "$detected_section" >> CLAUDE.md
418
541
  echo " Created CLAUDE.md"
419
542
  fi
420
543
  }
@@ -0,0 +1,94 @@
1
+ {
2
+ "projectType": "fastmcp",
3
+
4
+ "auth": {
5
+ "testUser": "",
6
+ "testPassword": "",
7
+ "loginEndpoint": "",
8
+ "loginMethod": "",
9
+ "tokenType": "",
10
+ "tokenHeader": "",
11
+ "tokenPrefix": ""
12
+ },
13
+
14
+ "docker": {
15
+ "enabled": true,
16
+ "composeFile": "docker-compose.yml",
17
+ "serviceName": "app",
18
+ "execPrefix": "docker compose exec -T"
19
+ },
20
+
21
+ "paths": {
22
+ "src": "src",
23
+ "tests": "tests",
24
+ "scripts": "scripts"
25
+ },
26
+
27
+ "commands": {
28
+ "dev": "python -m ${PROJECT_MODULE}.server",
29
+ "install": "uv pip install -e .",
30
+ "lint": "ruff check src/",
31
+ "format": "ruff format src/",
32
+ "typecheck": "mypy src/",
33
+ "test": "pytest",
34
+ "seed": "",
35
+ "resetDb": ""
36
+ },
37
+
38
+ "checks": {
39
+ "lint": true,
40
+ "typecheck": true,
41
+ "build": false,
42
+ "test": true,
43
+ "fastmcp": true
44
+ },
45
+
46
+ "api": {
47
+ "baseUrl": "http://localhost:8000",
48
+ "healthEndpoint": "/health",
49
+ "timeout": 30,
50
+ "transport": "sse"
51
+ },
52
+
53
+ "mcp": {
54
+ "transport": "stdio",
55
+ "serverModule": "",
56
+ "tools": [],
57
+ "resources": [],
58
+ "prompts": []
59
+ },
60
+
61
+ "playwright": {
62
+ "enabled": false,
63
+ "testDir": "tests/e2e",
64
+ "projects": ["chromium"],
65
+ "baseUrl": "http://localhost:8000"
66
+ },
67
+
68
+ "verification": {
69
+ "codeReviewEnabled": true,
70
+ "browserEnabled": false,
71
+ "mcpEnabled": true,
72
+ "screenshotOnFailure": false
73
+ },
74
+
75
+ "urls": {
76
+ "app": "http://localhost:8000",
77
+ "docs": ""
78
+ },
79
+
80
+ "env": {
81
+ "required": [],
82
+ "optional": []
83
+ },
84
+
85
+ "subprojects": {},
86
+
87
+ "maxIterations": 20,
88
+ "maxSessionSeconds": 600,
89
+
90
+ "contextRotThreshold": {
91
+ "maxStories": 10,
92
+ "maxFilesChanged": 20
93
+ }
94
+ }