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.
- package/.claude/commands/explain.md +114 -0
- package/.claude/commands/idea.md +370 -0
- package/.claude/commands/my-dna.md +122 -0
- package/.claude/commands/prd.md +286 -0
- package/.claude/commands/review.md +167 -0
- package/.claude/commands/sign.md +32 -0
- package/.claude/commands/styleguide.md +450 -0
- package/.claude/commands/tour.md +301 -0
- package/.claude/commands/vibe-check.md +116 -0
- package/.claude/commands/vibe-help.md +47 -0
- package/.claude/commands/vibe-list.md +203 -0
- package/.claude/settings.json +75 -0
- package/.claude/settings.local.json +12 -0
- package/.pre-commit-hooks.yaml +102 -0
- package/LICENSE +21 -0
- package/README.md +214 -0
- package/bin/postinstall.sh +29 -0
- package/bin/ralph.sh +171 -0
- package/bin/thrivekit.sh +24 -0
- package/bin/vibe-check.js +19 -0
- package/dist/checks/check-any-types.d.ts +6 -0
- package/dist/checks/check-any-types.d.ts.map +1 -0
- package/dist/checks/check-any-types.js +73 -0
- package/dist/checks/check-any-types.js.map +1 -0
- package/dist/checks/check-commented-code.d.ts +6 -0
- package/dist/checks/check-commented-code.d.ts.map +1 -0
- package/dist/checks/check-commented-code.js +81 -0
- package/dist/checks/check-commented-code.js.map +1 -0
- package/dist/checks/check-console-error.d.ts +6 -0
- package/dist/checks/check-console-error.d.ts.map +1 -0
- package/dist/checks/check-console-error.js +41 -0
- package/dist/checks/check-console-error.js.map +1 -0
- package/dist/checks/check-debug-statements.d.ts +6 -0
- package/dist/checks/check-debug-statements.d.ts.map +1 -0
- package/dist/checks/check-debug-statements.js +120 -0
- package/dist/checks/check-debug-statements.js.map +1 -0
- package/dist/checks/check-deep-nesting.d.ts +6 -0
- package/dist/checks/check-deep-nesting.d.ts.map +1 -0
- package/dist/checks/check-deep-nesting.js +116 -0
- package/dist/checks/check-deep-nesting.js.map +1 -0
- package/dist/checks/check-docker-platform.d.ts +6 -0
- package/dist/checks/check-docker-platform.d.ts.map +1 -0
- package/dist/checks/check-docker-platform.js +42 -0
- package/dist/checks/check-docker-platform.js.map +1 -0
- package/dist/checks/check-dry-violations.d.ts +6 -0
- package/dist/checks/check-dry-violations.d.ts.map +1 -0
- package/dist/checks/check-dry-violations.js +124 -0
- package/dist/checks/check-dry-violations.js.map +1 -0
- package/dist/checks/check-empty-catch.d.ts +6 -0
- package/dist/checks/check-empty-catch.d.ts.map +1 -0
- package/dist/checks/check-empty-catch.js +111 -0
- package/dist/checks/check-empty-catch.js.map +1 -0
- package/dist/checks/check-function-length.d.ts +6 -0
- package/dist/checks/check-function-length.d.ts.map +1 -0
- package/dist/checks/check-function-length.js +152 -0
- package/dist/checks/check-function-length.js.map +1 -0
- package/dist/checks/check-hardcoded-ai-models.d.ts +10 -0
- package/dist/checks/check-hardcoded-ai-models.d.ts.map +1 -0
- package/dist/checks/check-hardcoded-ai-models.js +102 -0
- package/dist/checks/check-hardcoded-ai-models.js.map +1 -0
- package/dist/checks/check-hardcoded-urls.d.ts +6 -0
- package/dist/checks/check-hardcoded-urls.d.ts.map +1 -0
- package/dist/checks/check-hardcoded-urls.js +124 -0
- package/dist/checks/check-hardcoded-urls.js.map +1 -0
- package/dist/checks/check-magic-numbers.d.ts +6 -0
- package/dist/checks/check-magic-numbers.d.ts.map +1 -0
- package/dist/checks/check-magic-numbers.js +116 -0
- package/dist/checks/check-magic-numbers.js.map +1 -0
- package/dist/checks/check-secrets.d.ts +6 -0
- package/dist/checks/check-secrets.d.ts.map +1 -0
- package/dist/checks/check-secrets.js +138 -0
- package/dist/checks/check-secrets.js.map +1 -0
- package/dist/checks/check-snake-case-ts.d.ts +6 -0
- package/dist/checks/check-snake-case-ts.d.ts.map +1 -0
- package/dist/checks/check-snake-case-ts.js +78 -0
- package/dist/checks/check-snake-case-ts.js.map +1 -0
- package/dist/checks/check-todo-fixme.d.ts +6 -0
- package/dist/checks/check-todo-fixme.d.ts.map +1 -0
- package/dist/checks/check-todo-fixme.js +41 -0
- package/dist/checks/check-todo-fixme.js.map +1 -0
- package/dist/checks/check-unsafe-html.d.ts +6 -0
- package/dist/checks/check-unsafe-html.d.ts.map +1 -0
- package/dist/checks/check-unsafe-html.js +101 -0
- package/dist/checks/check-unsafe-html.js.map +1 -0
- package/dist/checks/index.d.ts +30 -0
- package/dist/checks/index.d.ts.map +1 -0
- package/dist/checks/index.js +57 -0
- package/dist/checks/index.js.map +1 -0
- package/dist/cli.d.ts +13 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +206 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/file-reader.d.ts +24 -0
- package/dist/utils/file-reader.d.ts.map +1 -0
- package/dist/utils/file-reader.js +140 -0
- package/dist/utils/file-reader.js.map +1 -0
- package/dist/utils/patterns.d.ts +27 -0
- package/dist/utils/patterns.d.ts.map +1 -0
- package/dist/utils/patterns.js +84 -0
- package/dist/utils/patterns.js.map +1 -0
- package/dist/utils/reporters.d.ts +21 -0
- package/dist/utils/reporters.d.ts.map +1 -0
- package/dist/utils/reporters.js +115 -0
- package/dist/utils/reporters.js.map +1 -0
- package/dist/utils/types.d.ts +71 -0
- package/dist/utils/types.d.ts.map +1 -0
- package/dist/utils/types.js +5 -0
- package/dist/utils/types.js.map +1 -0
- package/package.json +82 -0
- package/ralph/api.sh +210 -0
- package/ralph/backup.sh +838 -0
- package/ralph/browser-verify/README.md +135 -0
- package/ralph/browser-verify/verify.ts +450 -0
- package/ralph/checks/check-fastapi-responses.py +155 -0
- package/ralph/hooks/hooks-config.json +72 -0
- package/ralph/hooks/inject-context.sh +44 -0
- package/ralph/hooks/install.sh +207 -0
- package/ralph/hooks/log-tools.sh +45 -0
- package/ralph/hooks/protect-prd.sh +27 -0
- package/ralph/hooks/save-learnings.sh +36 -0
- package/ralph/hooks/warn-debug.sh +54 -0
- package/ralph/hooks/warn-empty-catch.sh +63 -0
- package/ralph/hooks/warn-secrets.sh +89 -0
- package/ralph/hooks/warn-urls.sh +77 -0
- package/ralph/init.sh +388 -0
- package/ralph/loop.sh +570 -0
- package/ralph/playwright.sh +238 -0
- package/ralph/prd.sh +295 -0
- package/ralph/setup/feature-tour.sh +155 -0
- package/ralph/setup/quick-setup.sh +239 -0
- package/ralph/setup/tutorial.sh +159 -0
- package/ralph/setup/ui.sh +136 -0
- package/ralph/setup.sh +353 -0
- package/ralph/signs.sh +150 -0
- package/ralph/utils.sh +682 -0
- package/ralph/verify/browser.sh +324 -0
- package/ralph/verify/lint.sh +363 -0
- package/ralph/verify/review.sh +164 -0
- package/ralph/verify/tests.sh +81 -0
- package/ralph/verify.sh +224 -0
- package/templates/PROMPT.md +235 -0
- package/templates/config/fullstack.json +86 -0
- package/templates/config/go.json +81 -0
- package/templates/config/minimal.json +76 -0
- package/templates/config/node.json +81 -0
- package/templates/config/python.json +81 -0
- package/templates/config/rust.json +81 -0
- package/templates/examples/CLAUDE-django.md +174 -0
- package/templates/examples/CLAUDE-fastapi.md +270 -0
- package/templates/examples/CLAUDE-fastmcp.md +352 -0
- package/templates/examples/CLAUDE-fullstack.md +256 -0
- package/templates/examples/CLAUDE-node.md +246 -0
- package/templates/examples/CLAUDE-react.md +138 -0
- package/templates/optional/cursorrules.template +147 -0
- package/templates/optional/eslint.config.js +34 -0
- package/templates/optional/lint-staged.config.js +34 -0
- package/templates/optional/ruff.toml +125 -0
- package/templates/optional/vibe-check.yml +116 -0
- package/templates/optional/vscode-settings.json +127 -0
- 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
|
+
}
|