agentic-loop 3.19.0 → 3.21.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.
Files changed (51) hide show
  1. package/.claude/commands/tour.md +11 -7
  2. package/.claude/commands/vibe-help.md +5 -2
  3. package/.claude/commands/vibe-list.md +17 -2
  4. package/.claude/skills/prd/SKILL.md +21 -6
  5. package/.claude/skills/setup-review/SKILL.md +56 -0
  6. package/.claude/skills/tour/SKILL.md +11 -7
  7. package/.claude/skills/vibe-help/SKILL.md +2 -1
  8. package/.claude/skills/vibe-list/SKILL.md +5 -2
  9. package/.pre-commit-hooks.yaml +8 -0
  10. package/README.md +4 -0
  11. package/bin/agentic-loop.sh +7 -0
  12. package/bin/ralph.sh +29 -0
  13. package/dist/checks/check-signs-secrets.d.ts +9 -0
  14. package/dist/checks/check-signs-secrets.d.ts.map +1 -0
  15. package/dist/checks/check-signs-secrets.js +57 -0
  16. package/dist/checks/check-signs-secrets.js.map +1 -0
  17. package/dist/checks/index.d.ts +2 -5
  18. package/dist/checks/index.d.ts.map +1 -1
  19. package/dist/checks/index.js +4 -9
  20. package/dist/checks/index.js.map +1 -1
  21. package/dist/index.d.ts +1 -1
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +1 -1
  24. package/dist/index.js.map +1 -1
  25. package/package.json +2 -1
  26. package/ralph/hooks/common.sh +47 -0
  27. package/ralph/hooks/warn-debug.sh +12 -26
  28. package/ralph/hooks/warn-empty-catch.sh +21 -34
  29. package/ralph/hooks/warn-secrets.sh +39 -52
  30. package/ralph/hooks/warn-urls.sh +25 -45
  31. package/ralph/init.sh +58 -82
  32. package/ralph/loop.sh +506 -53
  33. package/ralph/prd-check.sh +177 -236
  34. package/ralph/prd.sh +5 -2
  35. package/ralph/setup/quick-setup.sh +2 -16
  36. package/ralph/setup.sh +68 -80
  37. package/ralph/signs.sh +8 -0
  38. package/ralph/uat.sh +2015 -0
  39. package/ralph/utils.sh +198 -69
  40. package/ralph/verify/tests.sh +65 -10
  41. package/templates/PROMPT.md +10 -4
  42. package/templates/UAT-PROMPT.md +197 -0
  43. package/templates/config/elixir.json +0 -2
  44. package/templates/config/fastmcp.json +0 -2
  45. package/templates/config/fullstack.json +2 -4
  46. package/templates/config/go.json +0 -2
  47. package/templates/config/minimal.json +0 -2
  48. package/templates/config/node.json +0 -2
  49. package/templates/config/python.json +0 -2
  50. package/templates/config/rust.json +0 -2
  51. package/templates/prd-example.json +6 -8
package/ralph/setup.sh CHANGED
@@ -76,6 +76,48 @@ append_prompt_sections() {
76
76
  fi
77
77
  }
78
78
 
79
+ # Migrate test credentials from config.json to .env
80
+ _migrate_credentials_to_env() {
81
+ local config=".ralph/config.json"
82
+ [[ ! -f "$config" ]] && return 0
83
+
84
+ local test_user test_password
85
+ test_user=$(jq -r '.auth.testUser // empty' "$config" 2>/dev/null)
86
+ test_password=$(jq -r '.auth.testPassword // empty' "$config" 2>/dev/null)
87
+
88
+ # Nothing to migrate
89
+ [[ -z "$test_user" && -z "$test_password" ]] && return 0
90
+
91
+ echo ""
92
+ print_info "Migrating test credentials from config.json to .env..."
93
+
94
+ # Write to .env
95
+ [[ ! -f ".env" ]] && touch ".env"
96
+
97
+ if grep -q "^RALPH_TEST_USER=" .env 2>/dev/null; then
98
+ local tmp
99
+ tmp=$(mktemp)
100
+ grep -v "^RALPH_TEST_USER=\|^RALPH_TEST_PASSWORD=" .env > "$tmp" && mv "$tmp" .env
101
+ fi
102
+
103
+ echo "" >> .env
104
+ echo "# Test credentials (migrated from config.json)" >> .env
105
+ [[ -n "$test_user" ]] && printf 'RALPH_TEST_USER=%s\n' "$test_user" >> .env
106
+ [[ -n "$test_password" ]] && printf 'RALPH_TEST_PASSWORD=%s\n' "$test_password" >> .env
107
+
108
+ # Remove from config.json
109
+ local tmpfile
110
+ tmpfile=$(mktemp)
111
+ if jq 'del(.auth.testUser, .auth.testPassword)' "$config" > "$tmpfile" 2>/dev/null; then
112
+ mv "$tmpfile" "$config"
113
+ print_success "Credentials moved to .env and removed from config.json"
114
+ else
115
+ rm -f "$tmpfile"
116
+ print_warning "Moved to .env but couldn't remove from config.json — edit manually"
117
+ fi
118
+ echo ""
119
+ }
120
+
79
121
  ralph_setup() {
80
122
  echo ""
81
123
  echo " _ _ _ _ "
@@ -119,6 +161,9 @@ ralph_setup() {
119
161
  fi
120
162
  fi
121
163
 
164
+ # Migrate credentials from config.json to .env if present
165
+ _migrate_credentials_to_env
166
+
122
167
  # Run all setup steps
123
168
  setup_ralph_dir "$pkg_root"
124
169
  setup_custom_checks
@@ -156,61 +201,19 @@ setup_ralph_dir() {
156
201
 
157
202
  # Copy config template based on detected project type
158
203
  if [[ ! -f ".ralph/config.json" ]]; then
159
- local config_template=""
160
204
  local detected_type=""
161
- # Check for Go projects
162
- if [[ -f "go.mod" ]]; then
163
- config_template="$pkg_root/templates/config/go.json"
164
- detected_type="go"
165
- # Check for Rust projects
166
- elif [[ -f "Cargo.toml" ]]; then
167
- config_template="$pkg_root/templates/config/rust.json"
168
- detected_type="rust"
169
- # Check for Hugo projects
170
- elif [[ -f "hugo.toml" || -f "hugo.yaml" || -f "config.toml" ]] && [[ -d "content" || -d "layouts" || -d "themes" ]]; then
171
- config_template="$pkg_root/templates/config/go.json"
172
- detected_type="hugo"
173
- # Check for Python framework variants (more specific first)
174
- elif [[ -f "pyproject.toml" ]]; then
175
- if grep -qiE "(fastmcp|\"fastmcp\"|'fastmcp')" pyproject.toml 2>/dev/null; then
176
- config_template="$pkg_root/templates/config/fastmcp.json"
177
- detected_type="fastmcp"
178
- elif grep -qiE "(fastapi|\"fastapi\"|'fastapi')" pyproject.toml 2>/dev/null; then
179
- config_template="$pkg_root/templates/config/python.json"
180
- detected_type="fastapi"
181
- elif grep -qiE "(django|\"django\"|'django')" pyproject.toml 2>/dev/null || [[ -f "manage.py" ]]; then
182
- config_template="$pkg_root/templates/config/python.json"
183
- detected_type="django"
184
- else
185
- config_template="$pkg_root/templates/config/python.json"
186
- detected_type="python"
187
- fi
188
- elif [[ -f "requirements.txt" ]]; then
189
- if grep -qi 'fastmcp' requirements.txt 2>/dev/null; then
190
- config_template="$pkg_root/templates/config/fastmcp.json"
191
- detected_type="fastmcp"
192
- elif grep -qi 'fastapi' requirements.txt 2>/dev/null; then
193
- config_template="$pkg_root/templates/config/python.json"
194
- detected_type="fastapi"
195
- elif grep -qi 'django' requirements.txt 2>/dev/null || [[ -f "manage.py" ]]; then
196
- config_template="$pkg_root/templates/config/python.json"
197
- detected_type="django"
198
- else
199
- config_template="$pkg_root/templates/config/python.json"
200
- detected_type="python"
201
- fi
202
- elif [[ -f "manage.py" ]]; then
203
- config_template="$pkg_root/templates/config/python.json"
204
- detected_type="django"
205
- # Check for fullstack projects
206
- elif [[ -d "frontend" ]] && [[ -d "backend" || -d "core" || -d "apps" ]]; then
207
- config_template="$pkg_root/templates/config/fullstack.json"
208
- detected_type="fullstack"
209
- # Check for Node projects
210
- elif [[ -f "package.json" ]]; then
211
- config_template="$pkg_root/templates/config/node.json"
212
- detected_type="node"
213
- fi
205
+ detected_type=$(detect_project_type)
206
+
207
+ # Map project type to config template
208
+ local config_template=""
209
+ case "$detected_type" in
210
+ go|hugo) config_template="$pkg_root/templates/config/go.json" ;;
211
+ rust) config_template="$pkg_root/templates/config/rust.json" ;;
212
+ fastmcp) config_template="$pkg_root/templates/config/fastmcp.json" ;;
213
+ django|fastapi|python) config_template="$pkg_root/templates/config/python.json" ;;
214
+ fullstack) config_template="$pkg_root/templates/config/fullstack.json" ;;
215
+ node) config_template="$pkg_root/templates/config/node.json" ;;
216
+ esac
214
217
 
215
218
  if [[ -n "$config_template" ]] && [[ -f "$config_template" ]]; then
216
219
  cp "$config_template" ".ralph/config.json"
@@ -492,32 +495,15 @@ setup_claude_md() {
492
495
  grep -q '"express"' "$pkg" 2>/dev/null && framework="${framework:+$framework + }Express"
493
496
  fi
494
497
 
495
- # Detect Python frameworks (more specific detection)
496
- if [[ -f "pyproject.toml" ]]; then
497
- if grep -qiE "(fastmcp|\"fastmcp\"|'fastmcp')" pyproject.toml 2>/dev/null; then
498
- framework="${framework:+$framework + }FastMCP"
499
- framework_type="fastmcp"
500
- elif grep -qiE "(fastapi|\"fastapi\"|'fastapi')" pyproject.toml 2>/dev/null; then
501
- framework="${framework:+$framework + }FastAPI"
502
- framework_type="fastapi"
503
- elif grep -qiE "(django|\"django\"|'django')" pyproject.toml 2>/dev/null || [[ -f "manage.py" ]]; then
504
- framework="${framework:+$framework + }Django"
505
- framework_type="django"
506
- fi
507
- elif [[ -f "requirements.txt" ]]; then
508
- if grep -qi 'fastmcp' requirements.txt 2>/dev/null; then
509
- framework="${framework:+$framework + }FastMCP"
510
- framework_type="fastmcp"
511
- elif grep -qi 'fastapi' requirements.txt 2>/dev/null; then
512
- framework="${framework:+$framework + }FastAPI"
513
- framework_type="fastapi"
514
- elif grep -qi 'django' requirements.txt 2>/dev/null || [[ -f "manage.py" ]]; then
515
- framework="${framework:+$framework + }Django"
516
- framework_type="django"
517
- fi
518
- elif [[ -f "manage.py" ]]; then
519
- framework="${framework:+$framework + }Django"
520
- framework_type="django"
498
+ # Detect Python/MCP frameworks
499
+ framework_type=$(detect_framework_type)
500
+ if [[ -n "$framework_type" ]]; then
501
+ case "$framework_type" in
502
+ fastmcp) framework="${framework:+$framework + }FastMCP" ;;
503
+ fastapi) framework="${framework:+$framework + }FastAPI" ;;
504
+ django) framework="${framework:+$framework + }Django" ;;
505
+ react) ;; # Already detected above from package.json
506
+ esac
521
507
  fi
522
508
 
523
509
  # Detect Hugo (Go static site generator)
@@ -718,6 +704,8 @@ repos:
718
704
  name: Check for hardcoded URLs
719
705
  - id: check-debug
720
706
  name: Check for debug statements
707
+ - id: check-signs-secrets
708
+ name: Check signs.json for credentials
721
709
  EOF
722
710
  echo " Created .pre-commit-config.yaml"
723
711
  else
package/ralph/signs.sh CHANGED
@@ -25,6 +25,14 @@ ralph_sign() {
25
25
  return 1
26
26
  fi
27
27
 
28
+ # Reject signs that contain credentials or secrets
29
+ if echo "$pattern" | grep -qiE '([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}|password[[:space:]]*[:=]|[[:space:]][A-Za-z0-9_]*_?(pass|pwd|token|secret|key|api.?key)[[:space:]]*[:=]|sk-[a-zA-Z0-9]{20,}|ghp_[a-zA-Z0-9]{36})'; then
30
+ print_error "Sign contains what looks like credentials (email, password, token, etc.)"
31
+ echo " Signs are saved to .ralph/signs.json which may be committed to git."
32
+ echo " Use environment variables instead of hardcoded credentials."
33
+ return 1
34
+ fi
35
+
28
36
  # Ensure signs.json exists
29
37
  if [[ ! -f "$RALPH_DIR/signs.json" ]]; then
30
38
  echo '{"signs": []}' > "$RALPH_DIR/signs.json"