thrivekit 2.0.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 (164) hide show
  1. package/.claude/commands/explain.md +114 -0
  2. package/.claude/commands/idea.md +370 -0
  3. package/.claude/commands/my-dna.md +122 -0
  4. package/.claude/commands/prd.md +286 -0
  5. package/.claude/commands/review.md +167 -0
  6. package/.claude/commands/sign.md +32 -0
  7. package/.claude/commands/styleguide.md +450 -0
  8. package/.claude/commands/tour.md +301 -0
  9. package/.claude/commands/vibe-check.md +116 -0
  10. package/.claude/commands/vibe-help.md +47 -0
  11. package/.claude/commands/vibe-list.md +203 -0
  12. package/.claude/settings.json +75 -0
  13. package/.claude/settings.local.json +12 -0
  14. package/.pre-commit-hooks.yaml +102 -0
  15. package/LICENSE +21 -0
  16. package/README.md +214 -0
  17. package/bin/postinstall.sh +29 -0
  18. package/bin/ralph.sh +171 -0
  19. package/bin/thrivekit.sh +24 -0
  20. package/bin/vibe-check.js +19 -0
  21. package/dist/checks/check-any-types.d.ts +6 -0
  22. package/dist/checks/check-any-types.d.ts.map +1 -0
  23. package/dist/checks/check-any-types.js +73 -0
  24. package/dist/checks/check-any-types.js.map +1 -0
  25. package/dist/checks/check-commented-code.d.ts +6 -0
  26. package/dist/checks/check-commented-code.d.ts.map +1 -0
  27. package/dist/checks/check-commented-code.js +81 -0
  28. package/dist/checks/check-commented-code.js.map +1 -0
  29. package/dist/checks/check-console-error.d.ts +6 -0
  30. package/dist/checks/check-console-error.d.ts.map +1 -0
  31. package/dist/checks/check-console-error.js +41 -0
  32. package/dist/checks/check-console-error.js.map +1 -0
  33. package/dist/checks/check-debug-statements.d.ts +6 -0
  34. package/dist/checks/check-debug-statements.d.ts.map +1 -0
  35. package/dist/checks/check-debug-statements.js +120 -0
  36. package/dist/checks/check-debug-statements.js.map +1 -0
  37. package/dist/checks/check-deep-nesting.d.ts +6 -0
  38. package/dist/checks/check-deep-nesting.d.ts.map +1 -0
  39. package/dist/checks/check-deep-nesting.js +116 -0
  40. package/dist/checks/check-deep-nesting.js.map +1 -0
  41. package/dist/checks/check-docker-platform.d.ts +6 -0
  42. package/dist/checks/check-docker-platform.d.ts.map +1 -0
  43. package/dist/checks/check-docker-platform.js +42 -0
  44. package/dist/checks/check-docker-platform.js.map +1 -0
  45. package/dist/checks/check-dry-violations.d.ts +6 -0
  46. package/dist/checks/check-dry-violations.d.ts.map +1 -0
  47. package/dist/checks/check-dry-violations.js +124 -0
  48. package/dist/checks/check-dry-violations.js.map +1 -0
  49. package/dist/checks/check-empty-catch.d.ts +6 -0
  50. package/dist/checks/check-empty-catch.d.ts.map +1 -0
  51. package/dist/checks/check-empty-catch.js +111 -0
  52. package/dist/checks/check-empty-catch.js.map +1 -0
  53. package/dist/checks/check-function-length.d.ts +6 -0
  54. package/dist/checks/check-function-length.d.ts.map +1 -0
  55. package/dist/checks/check-function-length.js +152 -0
  56. package/dist/checks/check-function-length.js.map +1 -0
  57. package/dist/checks/check-hardcoded-ai-models.d.ts +10 -0
  58. package/dist/checks/check-hardcoded-ai-models.d.ts.map +1 -0
  59. package/dist/checks/check-hardcoded-ai-models.js +102 -0
  60. package/dist/checks/check-hardcoded-ai-models.js.map +1 -0
  61. package/dist/checks/check-hardcoded-urls.d.ts +6 -0
  62. package/dist/checks/check-hardcoded-urls.d.ts.map +1 -0
  63. package/dist/checks/check-hardcoded-urls.js +124 -0
  64. package/dist/checks/check-hardcoded-urls.js.map +1 -0
  65. package/dist/checks/check-magic-numbers.d.ts +6 -0
  66. package/dist/checks/check-magic-numbers.d.ts.map +1 -0
  67. package/dist/checks/check-magic-numbers.js +116 -0
  68. package/dist/checks/check-magic-numbers.js.map +1 -0
  69. package/dist/checks/check-secrets.d.ts +6 -0
  70. package/dist/checks/check-secrets.d.ts.map +1 -0
  71. package/dist/checks/check-secrets.js +138 -0
  72. package/dist/checks/check-secrets.js.map +1 -0
  73. package/dist/checks/check-snake-case-ts.d.ts +6 -0
  74. package/dist/checks/check-snake-case-ts.d.ts.map +1 -0
  75. package/dist/checks/check-snake-case-ts.js +78 -0
  76. package/dist/checks/check-snake-case-ts.js.map +1 -0
  77. package/dist/checks/check-todo-fixme.d.ts +6 -0
  78. package/dist/checks/check-todo-fixme.d.ts.map +1 -0
  79. package/dist/checks/check-todo-fixme.js +41 -0
  80. package/dist/checks/check-todo-fixme.js.map +1 -0
  81. package/dist/checks/check-unsafe-html.d.ts +6 -0
  82. package/dist/checks/check-unsafe-html.d.ts.map +1 -0
  83. package/dist/checks/check-unsafe-html.js +101 -0
  84. package/dist/checks/check-unsafe-html.js.map +1 -0
  85. package/dist/checks/index.d.ts +30 -0
  86. package/dist/checks/index.d.ts.map +1 -0
  87. package/dist/checks/index.js +57 -0
  88. package/dist/checks/index.js.map +1 -0
  89. package/dist/cli.d.ts +13 -0
  90. package/dist/cli.d.ts.map +1 -0
  91. package/dist/cli.js +206 -0
  92. package/dist/cli.js.map +1 -0
  93. package/dist/index.d.ts +9 -0
  94. package/dist/index.d.ts.map +1 -0
  95. package/dist/index.js +10 -0
  96. package/dist/index.js.map +1 -0
  97. package/dist/utils/file-reader.d.ts +24 -0
  98. package/dist/utils/file-reader.d.ts.map +1 -0
  99. package/dist/utils/file-reader.js +140 -0
  100. package/dist/utils/file-reader.js.map +1 -0
  101. package/dist/utils/patterns.d.ts +27 -0
  102. package/dist/utils/patterns.d.ts.map +1 -0
  103. package/dist/utils/patterns.js +84 -0
  104. package/dist/utils/patterns.js.map +1 -0
  105. package/dist/utils/reporters.d.ts +21 -0
  106. package/dist/utils/reporters.d.ts.map +1 -0
  107. package/dist/utils/reporters.js +115 -0
  108. package/dist/utils/reporters.js.map +1 -0
  109. package/dist/utils/types.d.ts +71 -0
  110. package/dist/utils/types.d.ts.map +1 -0
  111. package/dist/utils/types.js +5 -0
  112. package/dist/utils/types.js.map +1 -0
  113. package/package.json +82 -0
  114. package/ralph/api.sh +210 -0
  115. package/ralph/backup.sh +838 -0
  116. package/ralph/browser-verify/README.md +135 -0
  117. package/ralph/browser-verify/verify.ts +450 -0
  118. package/ralph/checks/check-fastapi-responses.py +155 -0
  119. package/ralph/hooks/hooks-config.json +72 -0
  120. package/ralph/hooks/inject-context.sh +44 -0
  121. package/ralph/hooks/install.sh +207 -0
  122. package/ralph/hooks/log-tools.sh +45 -0
  123. package/ralph/hooks/protect-prd.sh +27 -0
  124. package/ralph/hooks/save-learnings.sh +36 -0
  125. package/ralph/hooks/warn-debug.sh +54 -0
  126. package/ralph/hooks/warn-empty-catch.sh +63 -0
  127. package/ralph/hooks/warn-secrets.sh +89 -0
  128. package/ralph/hooks/warn-urls.sh +77 -0
  129. package/ralph/init.sh +388 -0
  130. package/ralph/loop.sh +570 -0
  131. package/ralph/playwright.sh +238 -0
  132. package/ralph/prd.sh +295 -0
  133. package/ralph/setup/feature-tour.sh +155 -0
  134. package/ralph/setup/quick-setup.sh +239 -0
  135. package/ralph/setup/tutorial.sh +159 -0
  136. package/ralph/setup/ui.sh +136 -0
  137. package/ralph/setup.sh +353 -0
  138. package/ralph/signs.sh +150 -0
  139. package/ralph/utils.sh +682 -0
  140. package/ralph/verify/browser.sh +324 -0
  141. package/ralph/verify/lint.sh +363 -0
  142. package/ralph/verify/review.sh +164 -0
  143. package/ralph/verify/tests.sh +81 -0
  144. package/ralph/verify.sh +224 -0
  145. package/templates/PROMPT.md +235 -0
  146. package/templates/config/fullstack.json +86 -0
  147. package/templates/config/go.json +81 -0
  148. package/templates/config/minimal.json +76 -0
  149. package/templates/config/node.json +81 -0
  150. package/templates/config/python.json +81 -0
  151. package/templates/config/rust.json +81 -0
  152. package/templates/examples/CLAUDE-django.md +174 -0
  153. package/templates/examples/CLAUDE-fastapi.md +270 -0
  154. package/templates/examples/CLAUDE-fastmcp.md +352 -0
  155. package/templates/examples/CLAUDE-fullstack.md +256 -0
  156. package/templates/examples/CLAUDE-node.md +246 -0
  157. package/templates/examples/CLAUDE-react.md +138 -0
  158. package/templates/optional/cursorrules.template +147 -0
  159. package/templates/optional/eslint.config.js +34 -0
  160. package/templates/optional/lint-staged.config.js +34 -0
  161. package/templates/optional/ruff.toml +125 -0
  162. package/templates/optional/vibe-check.yml +116 -0
  163. package/templates/optional/vscode-settings.json +127 -0
  164. package/templates/signs.json +46 -0
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env bash
2
+ # warn-empty-catch.sh - Warn about empty catch blocks that silently swallow errors
3
+ # Hook: PostToolUse matcher: "Edit|Write"
4
+
5
+ set -euo pipefail
6
+
7
+ INPUT=$(cat)
8
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // ""')
9
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.path // ""')
10
+
11
+ # Only check code files
12
+ case "$FILE_PATH" in
13
+ *.ts|*.tsx|*.js|*.jsx|*.py)
14
+ ;;
15
+ *)
16
+ echo '{"continue": true}'
17
+ exit 0
18
+ ;;
19
+ esac
20
+
21
+ # Get the content that was written
22
+ NEW_CONTENT=""
23
+ if [[ "$TOOL_NAME" == "Write" ]]; then
24
+ NEW_CONTENT=$(echo "$INPUT" | jq -r '.tool_input.content // ""')
25
+ elif [[ "$TOOL_NAME" == "Edit" ]]; then
26
+ NEW_CONTENT=$(echo "$INPUT" | jq -r '.tool_input.new_string // ""')
27
+ fi
28
+
29
+ WARNINGS=""
30
+
31
+ # JavaScript/TypeScript: catch (e) { } or catch { }
32
+ if echo "$NEW_CONTENT" | grep -qE 'catch\s*\([^)]*\)\s*\{\s*\}'; then
33
+ WARNINGS="⚠️ Empty catch block detected. Handle the error or add a comment explaining why it's ignored."
34
+ fi
35
+
36
+ # Also check for catch with only a comment (no actual handling)
37
+ if echo "$NEW_CONTENT" | grep -qE 'catch\s*\([^)]*\)\s*\{\s*(//[^\n]*)?\s*\}'; then
38
+ if [[ -z "$WARNINGS" ]]; then
39
+ WARNINGS="⚠️ Catch block with no error handling. Consider logging or rethrowing the error."
40
+ fi
41
+ fi
42
+
43
+ # Python: except: pass or except Exception: pass
44
+ if echo "$NEW_CONTENT" | grep -qE 'except.*:\s*pass\s*$'; then
45
+ WARNINGS="⚠️ Empty except block (pass). Handle the exception or add logging."
46
+ fi
47
+
48
+ # Python: bare except with just pass on next line
49
+ if echo "$NEW_CONTENT" | grep -qE 'except.*:\s*$' && echo "$NEW_CONTENT" | grep -qE '^\s*pass\s*$'; then
50
+ if [[ -z "$WARNINGS" ]]; then
51
+ WARNINGS="⚠️ Except block with only 'pass'. Consider logging or reraising the exception."
52
+ fi
53
+ fi
54
+
55
+ # Block if empty catch detected
56
+ if [[ -n "$WARNINGS" ]]; then
57
+ jq -n --arg warn "$WARNINGS" '{
58
+ "continue": false,
59
+ "message": $warn
60
+ }'
61
+ else
62
+ echo '{"continue": true}'
63
+ fi
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env bash
2
+ # warn-secrets.sh - Warn about potential secrets in written code
3
+ # Hook: PostToolUse matcher: "Edit|Write"
4
+ #
5
+ # Mirrors the pre-commit check-secrets patterns for consistency
6
+
7
+ set -euo pipefail
8
+
9
+ INPUT=$(cat)
10
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // ""')
11
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.path // ""')
12
+
13
+ # Only check code files
14
+ case "$FILE_PATH" in
15
+ *.ts|*.tsx|*.js|*.jsx|*.py|*.json|*.yaml|*.yml|*.env*)
16
+ ;;
17
+ *)
18
+ echo '{"continue": true}'
19
+ exit 0
20
+ ;;
21
+ esac
22
+
23
+ # Get the content that was written
24
+ NEW_CONTENT=""
25
+ if [[ "$TOOL_NAME" == "Write" ]]; then
26
+ NEW_CONTENT=$(echo "$INPUT" | jq -r '.tool_input.content // ""')
27
+ elif [[ "$TOOL_NAME" == "Edit" ]]; then
28
+ NEW_CONTENT=$(echo "$INPUT" | jq -r '.tool_input.new_string // ""')
29
+ fi
30
+
31
+ WARNINGS=""
32
+
33
+ # AWS Access Key (AKIA followed by 16 alphanumeric chars)
34
+ if echo "$NEW_CONTENT" | grep -qE 'AKIA[0-9A-Z]{16}'; then
35
+ WARNINGS="🚨 SECURITY: Possible AWS Access Key detected! Use environment variables."
36
+ fi
37
+
38
+ # Stripe keys (sk_live_* or sk_test_*)
39
+ if echo "$NEW_CONTENT" | grep -qE 'sk_(live|test)_[0-9a-zA-Z]{24,}'; then
40
+ WARNINGS="${WARNINGS}\n🚨 SECURITY: Stripe API key detected! Use environment variables."
41
+ fi
42
+
43
+ # GitHub tokens (ghp_, gho_, ghu_, ghs_, ghr_)
44
+ if echo "$NEW_CONTENT" | grep -qE 'gh[pousr]_[A-Za-z0-9_]{36,}'; then
45
+ WARNINGS="${WARNINGS}\n🚨 SECURITY: GitHub token detected! Use environment variables."
46
+ fi
47
+
48
+ # Slack tokens (xoxb-, xoxp-, xoxa-, xoxr-, xoxs-)
49
+ if echo "$NEW_CONTENT" | grep -qE 'xox[baprs]-[0-9]{10,}-[0-9a-zA-Z]{24,}'; then
50
+ WARNINGS="${WARNINGS}\n🚨 SECURITY: Slack token detected! Use environment variables."
51
+ fi
52
+
53
+ # SendGrid keys (SG.*)
54
+ if echo "$NEW_CONTENT" | grep -qE 'SG\.[a-zA-Z0-9_-]{22}\.[a-zA-Z0-9_-]{43}'; then
55
+ WARNINGS="${WARNINGS}\n🚨 SECURITY: SendGrid API key detected! Use environment variables."
56
+ fi
57
+
58
+ # Private keys
59
+ if echo "$NEW_CONTENT" | grep -qE '-----BEGIN (RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----'; then
60
+ WARNINGS="${WARNINGS}\n🚨 SECURITY: Private key detected! Never commit private keys."
61
+ fi
62
+
63
+ # Generic API key patterns (api_key = "...", apikey: "...", etc.)
64
+ if echo "$NEW_CONTENT" | grep -qiE '(api[_-]?key|api[_-]?secret)\s*[:=]\s*['"'"'"][a-zA-Z0-9_\-]{20,}['"'"'"]'; then
65
+ # Check it's not a placeholder
66
+ if ! echo "$NEW_CONTENT" | grep -qiE '(example|placeholder|your[_-]?key|xxx|test|dummy|fake|sample|demo)'; then
67
+ WARNINGS="${WARNINGS}\n⚠️ Possible hardcoded API key - use environment variables."
68
+ fi
69
+ fi
70
+
71
+ # Generic secrets (password = "...", token = "...", etc.)
72
+ if echo "$NEW_CONTENT" | grep -qiE '(password|passwd|pwd|secret|token)\s*[:=]\s*['"'"'"][^'"'"'"]{8,}['"'"'"]'; then
73
+ # Check it's not a placeholder or type annotation
74
+ if ! echo "$NEW_CONTENT" | grep -qiE '(example|placeholder|xxx|test|dummy|type|interface|password:)'; then
75
+ WARNINGS="${WARNINGS}\n⚠️ Possible hardcoded secret - use environment variables."
76
+ fi
77
+ fi
78
+
79
+ # Output warning as additional context (non-blocking)
80
+ if [[ -n "$WARNINGS" ]]; then
81
+ jq -n --arg warn "$WARNINGS" '{
82
+ "continue": true,
83
+ "hookSpecificOutput": {
84
+ "additionalContext": $warn
85
+ }
86
+ }'
87
+ else
88
+ echo '{"continue": true}'
89
+ fi
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env bash
2
+ # warn-urls.sh - Warn about hardcoded URLs in written code
3
+ # Hook: PostToolUse matcher: "Edit|Write"
4
+ #
5
+ # Mirrors the pre-commit check-hardcoded-urls patterns for consistency
6
+
7
+ set -euo pipefail
8
+
9
+ INPUT=$(cat)
10
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // ""')
11
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.path // ""')
12
+
13
+ # Only check code files (skip test files)
14
+ case "$FILE_PATH" in
15
+ *.test.*|*.spec.*|*/__tests__/*|*/test/*|*/fixtures/*)
16
+ echo '{"continue": true}'
17
+ exit 0
18
+ ;;
19
+ *.ts|*.tsx|*.js|*.jsx|*.py)
20
+ ;;
21
+ *)
22
+ echo '{"continue": true}'
23
+ exit 0
24
+ ;;
25
+ esac
26
+
27
+ # Get the content that was written
28
+ NEW_CONTENT=""
29
+ if [[ "$TOOL_NAME" == "Write" ]]; then
30
+ NEW_CONTENT=$(echo "$INPUT" | jq -r '.tool_input.content // ""')
31
+ elif [[ "$TOOL_NAME" == "Edit" ]]; then
32
+ NEW_CONTENT=$(echo "$INPUT" | jq -r '.tool_input.new_string // ""')
33
+ fi
34
+
35
+ WARNINGS=""
36
+
37
+ # Check for localhost URLs
38
+ if echo "$NEW_CONTENT" | grep -qE 'https?://localhost(:[0-9]+)?'; then
39
+ # Skip if it's in a fallback pattern (|| or ?? or default)
40
+ if ! echo "$NEW_CONTENT" | grep -qE '(\|\||\?\?|default|fallback).*localhost'; then
41
+ WARNINGS="⚠️ Hardcoded localhost URL detected - use environment variable (e.g., process.env.API_URL)"
42
+ fi
43
+ fi
44
+
45
+ # Check for 127.0.0.1 URLs
46
+ if echo "$NEW_CONTENT" | grep -qE 'https?://127\.0\.0\.1(:[0-9]+)?'; then
47
+ if ! echo "$NEW_CONTENT" | grep -qE '(\|\||\?\?|default|fallback).*127\.0\.0\.1'; then
48
+ WARNINGS="${WARNINGS}\n⚠️ Hardcoded 127.0.0.1 URL detected - use environment variable"
49
+ fi
50
+ fi
51
+
52
+ # Check for hardcoded production-looking URLs (skip CDNs and common safe domains)
53
+ SAFE_DOMAINS="cdn.jsdelivr.net|cdnjs.cloudflare.com|unpkg.com|fonts.googleapis.com|fonts.gstatic.com|api.github.com|raw.githubusercontent.com|registry.npmjs.org|schema.org|www.w3.org|example.com|example.org"
54
+
55
+ # Look for https:// URLs that aren't safe domains
56
+ PROD_URLS=$(echo "$NEW_CONTENT" | grep -oE 'https://[a-zA-Z0-9][a-zA-Z0-9.-]+\.[a-zA-Z]{2,}' | grep -vE "$SAFE_DOMAINS" || true)
57
+
58
+ if [[ -n "$PROD_URLS" ]]; then
59
+ # Skip if they look like example/placeholder URLs
60
+ PROD_URLS=$(echo "$PROD_URLS" | grep -v -E '(example|placeholder|test|mock)' || true)
61
+ if [[ -n "$PROD_URLS" ]]; then
62
+ FIRST_URL=$(echo "$PROD_URLS" | head -1)
63
+ WARNINGS="${WARNINGS}\n⚠️ Hardcoded URL ($FIRST_URL) - consider using environment variable"
64
+ fi
65
+ fi
66
+
67
+ # Output warning as additional context (non-blocking)
68
+ if [[ -n "$WARNINGS" ]]; then
69
+ jq -n --arg warn "$WARNINGS" '{
70
+ "continue": true,
71
+ "hookSpecificOutput": {
72
+ "additionalContext": $warn
73
+ }
74
+ }'
75
+ else
76
+ echo '{"continue": true}'
77
+ fi
package/ralph/init.sh ADDED
@@ -0,0 +1,388 @@
1
+ #!/usr/bin/env bash
2
+ # shellcheck shell=bash
3
+ # init.sh - Initialize ralph in a project
4
+
5
+ ralph_init() {
6
+ # Check if already initialized (progress.txt is created by full init)
7
+ if [[ -f "$RALPH_DIR/progress.txt" ]]; then
8
+ echo "Ralph already initialized in this directory."
9
+ echo "Use 'ralph run' to start the loop or 'ralph status' to check status."
10
+ return 0
11
+ fi
12
+
13
+ echo "Initializing ralph..."
14
+
15
+ # Create directory structure
16
+ mkdir -p "$RALPH_DIR/archive" "$RALPH_DIR/screenshots"
17
+
18
+ # Detect project type and generate appropriate config
19
+ local project_type
20
+ project_type=$(detect_project_type)
21
+ echo "Detected project type: $project_type"
22
+
23
+ # Copy config template based on project type (only if missing)
24
+ if [[ ! -f "$RALPH_DIR/config.json" ]]; then
25
+ local config_template="$RALPH_TEMPLATES/config/${project_type}.json"
26
+ if [[ -f "$config_template" ]]; then
27
+ cp "$config_template" "$RALPH_DIR/config.json"
28
+ else
29
+ # Fall back to minimal config
30
+ cp "$RALPH_TEMPLATES/config/minimal.json" "$RALPH_DIR/config.json"
31
+ fi
32
+ fi
33
+
34
+ # Create signs with defaults (only if missing)
35
+ if [[ ! -f "$RALPH_DIR/signs.json" ]]; then
36
+ if [[ -f "$RALPH_TEMPLATES/signs.json" ]]; then
37
+ cp "$RALPH_TEMPLATES/signs.json" "$RALPH_DIR/signs.json"
38
+ else
39
+ echo '{"signs": []}' > "$RALPH_DIR/signs.json"
40
+ fi
41
+ fi
42
+
43
+ # Create progress log (only if missing)
44
+ if [[ ! -f "$RALPH_DIR/progress.txt" ]]; then
45
+ local timestamp
46
+ timestamp=$(date -Iseconds 2>/dev/null || date +%Y-%m-%dT%H:%M:%S)
47
+ echo "[$timestamp] INIT Ralph initialized" > "$RALPH_DIR/progress.txt"
48
+ fi
49
+
50
+ # Copy PROMPT.md template if it doesn't exist in project
51
+ if [[ ! -f "PROMPT.md" ]]; then
52
+ cp "$RALPH_TEMPLATES/PROMPT.md" "PROMPT.md"
53
+ echo "Created PROMPT.md template"
54
+ fi
55
+
56
+ # Auto-detect and configure project-specific settings
57
+ echo ""
58
+ echo "Auto-configuring project settings..."
59
+ auto_configure_project
60
+
61
+ print_success "Ralph initialized!"
62
+ echo ""
63
+
64
+ # Prompt for test credentials
65
+ configure_test_auth
66
+
67
+ echo ""
68
+ echo "Next steps:"
69
+ echo " 1. Review .ralph/config.json (test credentials, checks, etc.)"
70
+ echo " 2. Generate PRD:"
71
+ echo " - Thorough: /idea 'feature description' (brainstorm + architecture + scalability)"
72
+ echo " - Quick: ralph prd 'feature description' (basic PRD)"
73
+ echo " 3. Start loop: ralph run"
74
+ }
75
+
76
+ # Configure test authentication credentials
77
+ configure_test_auth() {
78
+ # Skip if not running in an interactive terminal
79
+ if [[ ! -t 0 ]]; then
80
+ return 0
81
+ fi
82
+
83
+ echo ""
84
+ print_info "=== Test Authentication Setup ==="
85
+ echo ""
86
+ echo "Ralph needs test credentials to verify authenticated endpoints."
87
+ echo "(You can skip this and edit .ralph/config.json later)"
88
+ echo ""
89
+
90
+ # Ask if they want to configure auth
91
+ read -p "Configure test credentials now? [y/N] " -n 1 -r
92
+ echo ""
93
+
94
+ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
95
+ print_info "Skipped. Edit .ralph/config.json to add credentials later."
96
+ return 0
97
+ fi
98
+
99
+ echo ""
100
+ read -p "Test user email/username: " test_user
101
+ read -s -p "Test user password: " test_password
102
+ echo ""
103
+
104
+ if [[ -z "$test_user" || -z "$test_password" ]]; then
105
+ print_warning "Credentials not provided."
106
+ echo " Options to add them later:"
107
+ echo " 1. Edit .ralph/config.json (stored in plain text)"
108
+ echo " 2. Set RALPH_TEST_USER and RALPH_TEST_PASSWORD env vars (recommended)"
109
+ return 0
110
+ fi
111
+
112
+ # Update config.json with credentials
113
+ local config="$RALPH_DIR/config.json"
114
+ if [[ -f "$config" ]]; then
115
+ local tmpfile
116
+ tmpfile=$(mktemp)
117
+ if jq --arg user "$test_user" --arg pass "$test_password" \
118
+ '.auth.testUser = $user | .auth.testPassword = $pass' \
119
+ "$config" > "$tmpfile" 2>/dev/null; then
120
+ mv "$tmpfile" "$config"
121
+ print_success "Test credentials saved to .ralph/config.json"
122
+ print_warning "Note: Credentials stored in plain text. Consider using env vars instead:"
123
+ echo " export RALPH_TEST_USER='$test_user'"
124
+ echo " export RALPH_TEST_PASSWORD='****'"
125
+ else
126
+ rm -f "$tmpfile"
127
+ print_warning "Failed to update config. Edit .ralph/config.json manually."
128
+ fi
129
+ fi
130
+ }
131
+
132
+ # Detect the type of project based on files present
133
+ detect_project_type() {
134
+ local project_type="minimal"
135
+
136
+ # Check for fullstack patterns first (more specific)
137
+ if [[ -d "frontend" && -d "core" ]]; then
138
+ project_type="fullstack"
139
+ elif [[ -d "frontend" && -d "backend" ]]; then
140
+ project_type="fullstack"
141
+ elif [[ -d "apps" ]]; then
142
+ project_type="fullstack" # Monorepo
143
+ # Then check for single-language projects
144
+ elif [[ -f "Cargo.toml" ]]; then
145
+ project_type="rust"
146
+ elif [[ -f "go.mod" ]]; then
147
+ project_type="go"
148
+ elif [[ -f "pyproject.toml" || -f "requirements.txt" || -f "setup.py" ]]; then
149
+ project_type="python"
150
+ elif [[ -f "package.json" ]]; then
151
+ project_type="node"
152
+ fi
153
+
154
+ echo "$project_type"
155
+ }
156
+
157
+ # Auto-detect and configure project-specific settings
158
+ auto_configure_project() {
159
+ local config="$RALPH_DIR/config.json"
160
+ [[ ! -f "$config" ]] && return 0
161
+
162
+ local updated=false
163
+ local tmpfile
164
+ tmpfile=$(mktemp)
165
+ cp "$config" "$tmpfile"
166
+
167
+ # 1. Detect Playwright test directory
168
+ local playwright_dir=""
169
+ for dir in "tests/e2e" "e2e" "test/e2e" \
170
+ "apps/web/tests/e2e" "apps/frontend/tests/e2e" \
171
+ "frontend/tests/e2e" "frontend/e2e" \
172
+ "packages/web/tests/e2e"; do
173
+ if [[ -d "$dir" ]]; then
174
+ playwright_dir="$dir"
175
+ break
176
+ fi
177
+ done
178
+
179
+ if [[ -n "$playwright_dir" ]]; then
180
+ if jq -e '.playwright.testDir' "$tmpfile" >/dev/null 2>&1; then
181
+ : # Already set
182
+ else
183
+ jq --arg dir "$playwright_dir" '.playwright.testDir = $dir' "$tmpfile" > "${tmpfile}.new" && mv "${tmpfile}.new" "$tmpfile"
184
+ echo " Auto-detected playwright.testDir: $playwright_dir"
185
+ updated=true
186
+ fi
187
+ fi
188
+
189
+ # 2. Detect testUrlBase from common patterns
190
+ local base_url=""
191
+ # Check package.json scripts for dev server port
192
+ if [[ -f "package.json" ]]; then
193
+ if grep -q '"dev".*:3000' package.json 2>/dev/null; then
194
+ base_url="http://localhost:3000"
195
+ elif grep -q '"dev".*:5173' package.json 2>/dev/null; then
196
+ base_url="http://localhost:5173" # Vite default
197
+ elif grep -q '"dev".*:8080' package.json 2>/dev/null; then
198
+ base_url="http://localhost:8080"
199
+ fi
200
+ fi
201
+ # Check for monorepo frontend
202
+ for fe_pkg in "apps/web/package.json" "apps/frontend/package.json" "frontend/package.json"; do
203
+ if [[ -f "$fe_pkg" && -z "$base_url" ]]; then
204
+ if grep -q ':3000' "$fe_pkg" 2>/dev/null; then
205
+ base_url="http://localhost:3000"
206
+ elif grep -q ':5173' "$fe_pkg" 2>/dev/null; then
207
+ base_url="http://localhost:5173"
208
+ fi
209
+ fi
210
+ done
211
+
212
+ if [[ -n "$base_url" ]]; then
213
+ if jq -e '.testUrlBase' "$tmpfile" >/dev/null 2>&1 && [[ "$(jq -r '.testUrlBase' "$tmpfile")" != "" ]]; then
214
+ : # Already set
215
+ else
216
+ jq --arg url "$base_url" '.testUrlBase = $url' "$tmpfile" > "${tmpfile}.new" && mv "${tmpfile}.new" "$tmpfile"
217
+ echo " Auto-detected testUrlBase: $base_url"
218
+ updated=true
219
+ fi
220
+ fi
221
+
222
+ # 3. Detect frontend/backend directories for monorepos
223
+ local frontend_dir="" backend_dir=""
224
+ for dir in "apps/web" "apps/frontend" "frontend" "packages/web" "web"; do
225
+ if [[ -d "$dir" && -f "$dir/package.json" ]]; then
226
+ frontend_dir="$dir"
227
+ break
228
+ fi
229
+ done
230
+ for dir in "apps/api" "apps/backend" "backend" "api" "server"; do
231
+ if [[ -d "$dir" ]] && ([[ -f "$dir/package.json" ]] || [[ -f "$dir/pyproject.toml" ]] || [[ -f "$dir/requirements.txt" ]]); then
232
+ backend_dir="$dir"
233
+ break
234
+ fi
235
+ done
236
+
237
+ if [[ -n "$frontend_dir" ]]; then
238
+ if jq -e '.directories.frontend' "$tmpfile" >/dev/null 2>&1; then
239
+ : # Already set
240
+ else
241
+ jq --arg dir "$frontend_dir" '.directories.frontend = $dir' "$tmpfile" > "${tmpfile}.new" && mv "${tmpfile}.new" "$tmpfile"
242
+ echo " Auto-detected directories.frontend: $frontend_dir"
243
+ updated=true
244
+ fi
245
+ fi
246
+
247
+ if [[ -n "$backend_dir" ]]; then
248
+ if jq -e '.directories.backend' "$tmpfile" >/dev/null 2>&1; then
249
+ : # Already set
250
+ else
251
+ jq --arg dir "$backend_dir" '.directories.backend = $dir' "$tmpfile" > "${tmpfile}.new" && mv "${tmpfile}.new" "$tmpfile"
252
+ echo " Auto-detected directories.backend: $backend_dir"
253
+ updated=true
254
+ fi
255
+ fi
256
+
257
+ # 4. Detect package manager
258
+ local pkg_manager="npm"
259
+ if [[ -f "pnpm-lock.yaml" ]]; then
260
+ pkg_manager="pnpm"
261
+ elif [[ -f "yarn.lock" ]]; then
262
+ pkg_manager="yarn"
263
+ elif [[ -f "bun.lockb" ]]; then
264
+ pkg_manager="bun"
265
+ fi
266
+
267
+ if [[ "$pkg_manager" != "npm" ]]; then
268
+ if jq -e '.packageManager' "$tmpfile" >/dev/null 2>&1; then
269
+ : # Already set
270
+ else
271
+ jq --arg pm "$pkg_manager" '.packageManager = $pm' "$tmpfile" > "${tmpfile}.new" && mv "${tmpfile}.new" "$tmpfile"
272
+ echo " Auto-detected packageManager: $pkg_manager"
273
+ updated=true
274
+ fi
275
+ fi
276
+
277
+ # Save if updated
278
+ if [[ "$updated" == "true" ]]; then
279
+ mv "$tmpfile" "$config"
280
+ else
281
+ rm -f "$tmpfile"
282
+ fi
283
+ }
284
+
285
+ # Show current ralph status
286
+ ralph_status() {
287
+ if [[ ! -d "$RALPH_DIR" ]]; then
288
+ print_error "Ralph not initialized. Run 'ralph init' first."
289
+ return 1
290
+ fi
291
+
292
+ echo ""
293
+ print_info "=== Ralph Status ==="
294
+ echo ""
295
+
296
+ # Check if PRD exists
297
+ if [[ -f "$RALPH_DIR/prd.json" ]]; then
298
+ local feature_name status
299
+ feature_name=$(jq -r '.feature.name // "Unknown"' "$RALPH_DIR/prd.json")
300
+ status=$(jq -r '.feature.status // "unknown"' "$RALPH_DIR/prd.json")
301
+
302
+ echo "Feature: $feature_name"
303
+ echo "Status: $status"
304
+ echo ""
305
+
306
+ # Show stories
307
+ echo "Stories:"
308
+ jq -r '.stories[] | " \(.id): \(.title) [\(if .passes then "DONE" else "TODO" end)]"' "$RALPH_DIR/prd.json" 2>/dev/null || echo " (none)"
309
+
310
+ # Count pass/fail
311
+ local total passed failed
312
+ total=$(jq '.stories | length' "$RALPH_DIR/prd.json")
313
+ passed=$(jq '[.stories[] | select(.passes == true)] | length' "$RALPH_DIR/prd.json")
314
+ failed=$((total - passed))
315
+ echo ""
316
+ echo "Progress: $passed/$total passed ($failed remaining)"
317
+ else
318
+ # Check for misplaced PRD in subdirectories
319
+ local found_prd
320
+ found_prd=$(find . -path "./.ralph" -prune -o -name "prd.json" -path "*/.ralph/prd.json" -print 2>/dev/null | head -1)
321
+
322
+ if [[ -n "$found_prd" ]]; then
323
+ print_warning "PRD found in wrong location: $found_prd"
324
+ echo ""
325
+ echo "Move it to root with:"
326
+ echo " mv $found_prd .ralph/prd.json"
327
+ else
328
+ echo "No active PRD. Generate one with: ralph prd 'your feature notes...'"
329
+ fi
330
+ fi
331
+
332
+ echo ""
333
+
334
+ # Show recent progress
335
+ if [[ -f "$RALPH_DIR/progress.txt" ]]; then
336
+ echo "Recent activity:"
337
+ tail -5 "$RALPH_DIR/progress.txt" | sed 's/^/ /'
338
+ fi
339
+
340
+ echo ""
341
+ }
342
+
343
+ # Show help
344
+ ralph_help() {
345
+ cat <<'EOF'
346
+ thrivekit - Tools to thrive with agentic coding
347
+
348
+ Usage:
349
+ npx thrivekit <command> [options]
350
+
351
+ Commands:
352
+ setup Set up project (hooks, config, CLAUDE.md)
353
+ init Initialize Ralph in current directory
354
+ config Re-detect and update project config
355
+ prd <notes> Generate PRD interactively (quick mode)
356
+ prd --file <file> Generate PRD from file
357
+ run Run autonomous loop until all stories pass
358
+ run --max <n> Run with max iterations (default: 20)
359
+ status Show current feature and story status
360
+ check Run verification checks only
361
+ verify <story-id> Verify a specific story
362
+ sign <pattern> [cat] Add a learned pattern (sign)
363
+ signs List all learned patterns
364
+ backup Backup detected databases to .backups/
365
+ backups List available database backups
366
+ restore <path> Restore database from backup
367
+ help Show this help message
368
+
369
+ PRD Generation:
370
+ /idea <description> Thorough brainstorm (in Claude Code)
371
+ npx thrivekit prd <notes> Quick PRD generation
372
+
373
+ Examples:
374
+ npm install thrivekit && npx thrivekit setup
375
+ /idea "Add user authentication with OAuth"
376
+ npx thrivekit prd "Add a contact form"
377
+ npx thrivekit run
378
+ npx thrivekit run --max 10
379
+ npx thrivekit status
380
+ npx thrivekit sign "Always use camelCase" frontend
381
+
382
+ Environment:
383
+ RALPH_DIR Override .ralph directory location (default: .ralph)
384
+ PROMPT_FILE Override PROMPT.md location (default: PROMPT.md)
385
+
386
+ For more information, see: https://github.com/allthriveai/thrivekit
387
+ EOF
388
+ }