agentic-loop 1.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 +398 -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/.pre-commit-hooks.yaml +102 -0
- package/LICENSE +21 -0
- package/README.md +238 -0
- package/bin/agentic-loop.sh +24 -0
- package/bin/postinstall.sh +29 -0
- package/bin/ralph.sh +171 -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 +208 -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 +146 -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 +83 -0
- package/ralph/api.sh +216 -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 +515 -0
- package/ralph/loop.sh +730 -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 +401 -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 +152 -0
- package/ralph/verify/tests.sh +81 -0
- package/ralph/verify.sh +268 -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
package/ralph/init.sh
ADDED
|
@@ -0,0 +1,515 @@
|
|
|
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 by parsing actual config files for port values
|
|
190
|
+
local base_url=""
|
|
191
|
+
local web_port=""
|
|
192
|
+
|
|
193
|
+
# Priority 1: docker-compose.yml - parse actual port mappings
|
|
194
|
+
for compose_file in "docker-compose.yml" "docker-compose.yaml" "compose.yml" "compose.yaml"; do
|
|
195
|
+
if [[ -f "$compose_file" && -z "$web_port" ]]; then
|
|
196
|
+
# Extract port from web/frontend service: "5173:5173" -> 5173
|
|
197
|
+
web_port=$(grep -A 20 -E '^\s*(web|frontend|client|ui):' "$compose_file" 2>/dev/null | \
|
|
198
|
+
grep -E '^\s*-\s*"?[0-9]+:[0-9]+"?' | head -1 | \
|
|
199
|
+
grep -oE '[0-9]+:' | head -1 | tr -d ':')
|
|
200
|
+
fi
|
|
201
|
+
done
|
|
202
|
+
|
|
203
|
+
# Priority 2: Vite config - parse server.port
|
|
204
|
+
if [[ -z "$web_port" ]]; then
|
|
205
|
+
for vite_config in "vite.config.ts" "vite.config.js" "apps/web/vite.config.ts" "apps/web/vite.config.js" \
|
|
206
|
+
"apps/frontend/vite.config.ts" "frontend/vite.config.ts"; do
|
|
207
|
+
if [[ -f "$vite_config" ]]; then
|
|
208
|
+
# Look for port: 3000 or port: '3000'
|
|
209
|
+
web_port=$(grep -E 'port\s*[=:]\s*[0-9]+' "$vite_config" 2>/dev/null | grep -oE '[0-9]{4}' | head -1)
|
|
210
|
+
[[ -z "$web_port" ]] && web_port="5173" # Vite's documented default
|
|
211
|
+
break
|
|
212
|
+
fi
|
|
213
|
+
done
|
|
214
|
+
fi
|
|
215
|
+
|
|
216
|
+
# Priority 3: Hugo config - parse server.port or use documented default
|
|
217
|
+
if [[ -z "$web_port" ]]; then
|
|
218
|
+
for hugo_config in "hugo.toml" "hugo.yaml" "hugo.json" "config.toml"; do
|
|
219
|
+
if [[ -f "$hugo_config" ]] && [[ -d "content" || -d "layouts" || -d "themes" ]]; then
|
|
220
|
+
web_port=$(grep -E 'port\s*=' "$hugo_config" 2>/dev/null | grep -oE '[0-9]+' | head -1)
|
|
221
|
+
[[ -z "$web_port" ]] && web_port="1313" # Hugo's documented default
|
|
222
|
+
break
|
|
223
|
+
fi
|
|
224
|
+
done
|
|
225
|
+
fi
|
|
226
|
+
|
|
227
|
+
# Priority 4: Next.js - check package.json dev script for -p flag
|
|
228
|
+
if [[ -z "$web_port" ]]; then
|
|
229
|
+
for next_config in "next.config.js" "next.config.ts" "next.config.mjs" \
|
|
230
|
+
"apps/web/next.config.js" "apps/web/next.config.mjs"; do
|
|
231
|
+
if [[ -f "$next_config" ]]; then
|
|
232
|
+
# Check if package.json has custom port: "dev": "next dev -p 4000"
|
|
233
|
+
local pkg_dir
|
|
234
|
+
pkg_dir=$(dirname "$next_config")
|
|
235
|
+
if [[ -f "$pkg_dir/package.json" ]]; then
|
|
236
|
+
web_port=$(grep -E '"dev".*-p\s*[0-9]+' "$pkg_dir/package.json" 2>/dev/null | grep -oE '\-p\s*[0-9]+' | grep -oE '[0-9]+')
|
|
237
|
+
fi
|
|
238
|
+
[[ -z "$web_port" ]] && web_port="3000" # Next.js documented default
|
|
239
|
+
break
|
|
240
|
+
fi
|
|
241
|
+
done
|
|
242
|
+
fi
|
|
243
|
+
|
|
244
|
+
# Priority 5: Generic package.json port detection
|
|
245
|
+
if [[ -z "$web_port" && -f "package.json" ]]; then
|
|
246
|
+
# Look for explicit port in scripts: --port 3000, -p 3000, :3000
|
|
247
|
+
web_port=$(grep -E '"(dev|start|serve)"' package.json 2>/dev/null | grep -oE '(--port|-p|:)[[:space:]]*[0-9]{4}' | grep -oE '[0-9]{4}' | head -1)
|
|
248
|
+
fi
|
|
249
|
+
|
|
250
|
+
if [[ -n "$web_port" ]]; then
|
|
251
|
+
base_url="http://localhost:$web_port"
|
|
252
|
+
fi
|
|
253
|
+
|
|
254
|
+
if [[ -n "$base_url" ]]; then
|
|
255
|
+
if jq -e '.testUrlBase' "$tmpfile" >/dev/null 2>&1 && [[ "$(jq -r '.testUrlBase' "$tmpfile")" != "" ]]; then
|
|
256
|
+
: # Already set
|
|
257
|
+
else
|
|
258
|
+
jq --arg url "$base_url" '.testUrlBase = $url' "$tmpfile" > "${tmpfile}.new" && mv "${tmpfile}.new" "$tmpfile"
|
|
259
|
+
echo " Auto-detected testUrlBase: $base_url"
|
|
260
|
+
updated=true
|
|
261
|
+
fi
|
|
262
|
+
fi
|
|
263
|
+
|
|
264
|
+
# 3. Detect frontend/backend directories for monorepos
|
|
265
|
+
local frontend_dir="" backend_dir=""
|
|
266
|
+
for dir in "apps/web" "apps/frontend" "frontend" "packages/web" "web"; do
|
|
267
|
+
if [[ -d "$dir" && -f "$dir/package.json" ]]; then
|
|
268
|
+
frontend_dir="$dir"
|
|
269
|
+
break
|
|
270
|
+
fi
|
|
271
|
+
done
|
|
272
|
+
for dir in "apps/api" "apps/backend" "backend" "api" "server"; do
|
|
273
|
+
if [[ -d "$dir" ]] && ([[ -f "$dir/package.json" ]] || [[ -f "$dir/pyproject.toml" ]] || [[ -f "$dir/requirements.txt" ]]); then
|
|
274
|
+
backend_dir="$dir"
|
|
275
|
+
break
|
|
276
|
+
fi
|
|
277
|
+
done
|
|
278
|
+
|
|
279
|
+
if [[ -n "$frontend_dir" ]]; then
|
|
280
|
+
if jq -e '.directories.frontend' "$tmpfile" >/dev/null 2>&1; then
|
|
281
|
+
: # Already set
|
|
282
|
+
else
|
|
283
|
+
jq --arg dir "$frontend_dir" '.directories.frontend = $dir' "$tmpfile" > "${tmpfile}.new" && mv "${tmpfile}.new" "$tmpfile"
|
|
284
|
+
echo " Auto-detected directories.frontend: $frontend_dir"
|
|
285
|
+
updated=true
|
|
286
|
+
fi
|
|
287
|
+
fi
|
|
288
|
+
|
|
289
|
+
if [[ -n "$backend_dir" ]]; then
|
|
290
|
+
if jq -e '.directories.backend' "$tmpfile" >/dev/null 2>&1; then
|
|
291
|
+
: # Already set
|
|
292
|
+
else
|
|
293
|
+
jq --arg dir "$backend_dir" '.directories.backend = $dir' "$tmpfile" > "${tmpfile}.new" && mv "${tmpfile}.new" "$tmpfile"
|
|
294
|
+
echo " Auto-detected directories.backend: $backend_dir"
|
|
295
|
+
updated=true
|
|
296
|
+
fi
|
|
297
|
+
fi
|
|
298
|
+
|
|
299
|
+
# 4. Detect API baseUrl by parsing actual config files for port values
|
|
300
|
+
local api_url=""
|
|
301
|
+
local api_port=""
|
|
302
|
+
|
|
303
|
+
# Priority 1: docker-compose.yml - parse actual port mappings
|
|
304
|
+
for compose_file in "docker-compose.yml" "docker-compose.yaml" "compose.yml" "compose.yaml"; do
|
|
305
|
+
if [[ -f "$compose_file" && -z "$api_port" ]]; then
|
|
306
|
+
# Extract port from api/backend service: "8000:8000" -> 8000
|
|
307
|
+
api_port=$(grep -A 20 -E '^\s*(api|backend|server|app):' "$compose_file" 2>/dev/null | \
|
|
308
|
+
grep -E '^\s*-\s*"?[0-9]+:[0-9]+"?' | head -1 | \
|
|
309
|
+
grep -oE '[0-9]+:' | head -1 | tr -d ':')
|
|
310
|
+
fi
|
|
311
|
+
done
|
|
312
|
+
|
|
313
|
+
# Priority 2: Dockerfile EXPOSE directive
|
|
314
|
+
if [[ -n "$backend_dir" && -z "$api_port" ]]; then
|
|
315
|
+
if [[ -f "$backend_dir/Dockerfile" ]]; then
|
|
316
|
+
api_port=$(grep -i "^EXPOSE" "$backend_dir/Dockerfile" 2>/dev/null | head -1 | grep -oE '[0-9]+' | head -1)
|
|
317
|
+
fi
|
|
318
|
+
fi
|
|
319
|
+
|
|
320
|
+
# Priority 3: Python - check for uvicorn/gunicorn port in scripts or pyproject.toml
|
|
321
|
+
if [[ -n "$backend_dir" && -z "$api_port" ]]; then
|
|
322
|
+
if [[ -f "$backend_dir/pyproject.toml" ]] || [[ -f "$backend_dir/requirements.txt" ]]; then
|
|
323
|
+
# Check pyproject.toml for port config
|
|
324
|
+
if [[ -f "$backend_dir/pyproject.toml" ]]; then
|
|
325
|
+
api_port=$(grep -E 'port\s*=' "$backend_dir/pyproject.toml" 2>/dev/null | grep -oE '[0-9]+' | head -1)
|
|
326
|
+
fi
|
|
327
|
+
# Check for uvicorn command with --port
|
|
328
|
+
if [[ -z "$api_port" && -f "$backend_dir/Makefile" ]]; then
|
|
329
|
+
api_port=$(grep -E 'uvicorn.*--port' "$backend_dir/Makefile" 2>/dev/null | grep -oE '\-\-port[[:space:]]*[0-9]+' | grep -oE '[0-9]+' | head -1)
|
|
330
|
+
fi
|
|
331
|
+
# Uvicorn/FastAPI default
|
|
332
|
+
[[ -z "$api_port" ]] && api_port="8000"
|
|
333
|
+
fi
|
|
334
|
+
fi
|
|
335
|
+
|
|
336
|
+
# Priority 4: Node backend - check package.json for port
|
|
337
|
+
if [[ -n "$backend_dir" && -z "$api_port" && -f "$backend_dir/package.json" ]]; then
|
|
338
|
+
api_port=$(grep -E '"(dev|start|serve)"' "$backend_dir/package.json" 2>/dev/null | grep -oE '(--port|-p|:)[[:space:]]*[0-9]{4}' | grep -oE '[0-9]{4}' | head -1)
|
|
339
|
+
fi
|
|
340
|
+
|
|
341
|
+
if [[ -n "$api_port" ]]; then
|
|
342
|
+
api_url="http://localhost:$api_port"
|
|
343
|
+
fi
|
|
344
|
+
|
|
345
|
+
if [[ -n "$api_url" ]]; then
|
|
346
|
+
if jq -e '.api.baseUrl' "$tmpfile" >/dev/null 2>&1 && [[ "$(jq -r '.api.baseUrl' "$tmpfile")" != "" ]]; then
|
|
347
|
+
: # Already set
|
|
348
|
+
else
|
|
349
|
+
jq --arg url "$api_url" '.api.baseUrl = $url' "$tmpfile" > "${tmpfile}.new" && mv "${tmpfile}.new" "$tmpfile"
|
|
350
|
+
echo " Auto-detected api.baseUrl: $api_url"
|
|
351
|
+
updated=true
|
|
352
|
+
fi
|
|
353
|
+
fi
|
|
354
|
+
|
|
355
|
+
# 5. Detect package manager
|
|
356
|
+
local pkg_manager="npm"
|
|
357
|
+
if [[ -f "pnpm-lock.yaml" ]]; then
|
|
358
|
+
pkg_manager="pnpm"
|
|
359
|
+
elif [[ -f "yarn.lock" ]]; then
|
|
360
|
+
pkg_manager="yarn"
|
|
361
|
+
elif [[ -f "bun.lockb" ]]; then
|
|
362
|
+
pkg_manager="bun"
|
|
363
|
+
fi
|
|
364
|
+
|
|
365
|
+
if [[ "$pkg_manager" != "npm" ]]; then
|
|
366
|
+
if jq -e '.packageManager' "$tmpfile" >/dev/null 2>&1; then
|
|
367
|
+
: # Already set
|
|
368
|
+
else
|
|
369
|
+
jq --arg pm "$pkg_manager" '.packageManager = $pm' "$tmpfile" > "${tmpfile}.new" && mv "${tmpfile}.new" "$tmpfile"
|
|
370
|
+
echo " Auto-detected packageManager: $pkg_manager"
|
|
371
|
+
updated=true
|
|
372
|
+
fi
|
|
373
|
+
fi
|
|
374
|
+
|
|
375
|
+
# Save if updated
|
|
376
|
+
if [[ "$updated" == "true" ]]; then
|
|
377
|
+
mv "$tmpfile" "$config"
|
|
378
|
+
else
|
|
379
|
+
rm -f "$tmpfile"
|
|
380
|
+
fi
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
# Show current ralph status
|
|
384
|
+
ralph_status() {
|
|
385
|
+
if [[ ! -d "$RALPH_DIR" ]]; then
|
|
386
|
+
print_error "Ralph not initialized. Run 'ralph init' first."
|
|
387
|
+
return 1
|
|
388
|
+
fi
|
|
389
|
+
|
|
390
|
+
echo ""
|
|
391
|
+
print_info "=== Ralph Status ==="
|
|
392
|
+
echo ""
|
|
393
|
+
|
|
394
|
+
# Check if PRD exists
|
|
395
|
+
if [[ -f "$RALPH_DIR/prd.json" ]]; then
|
|
396
|
+
local feature_name status
|
|
397
|
+
feature_name=$(jq -r '.feature.name // "Unknown"' "$RALPH_DIR/prd.json")
|
|
398
|
+
status=$(jq -r '.feature.status // "unknown"' "$RALPH_DIR/prd.json")
|
|
399
|
+
|
|
400
|
+
echo "Feature: $feature_name"
|
|
401
|
+
echo "Status: $status"
|
|
402
|
+
echo ""
|
|
403
|
+
|
|
404
|
+
# Show stories
|
|
405
|
+
echo "Stories:"
|
|
406
|
+
jq -r '.stories[] | " \(.id): \(.title) [\(if .passes then "DONE" else "TODO" end)]"' "$RALPH_DIR/prd.json" 2>/dev/null || echo " (none)"
|
|
407
|
+
|
|
408
|
+
# Count pass/fail
|
|
409
|
+
local total passed failed
|
|
410
|
+
total=$(jq '.stories | length' "$RALPH_DIR/prd.json")
|
|
411
|
+
passed=$(jq '[.stories[] | select(.passes == true)] | length' "$RALPH_DIR/prd.json")
|
|
412
|
+
failed=$((total - passed))
|
|
413
|
+
echo ""
|
|
414
|
+
echo "Progress: $passed/$total passed ($failed remaining)"
|
|
415
|
+
else
|
|
416
|
+
# Check for misplaced PRD in subdirectories
|
|
417
|
+
local found_prd
|
|
418
|
+
found_prd=$(find . -path "./.ralph" -prune -o -name "prd.json" -path "*/.ralph/prd.json" -print 2>/dev/null | head -1)
|
|
419
|
+
|
|
420
|
+
if [[ -n "$found_prd" ]]; then
|
|
421
|
+
print_warning "PRD found in wrong location: $found_prd"
|
|
422
|
+
echo ""
|
|
423
|
+
echo "Move it to root with:"
|
|
424
|
+
echo " mv $found_prd .ralph/prd.json"
|
|
425
|
+
else
|
|
426
|
+
echo "No active PRD. Generate one with: ralph prd 'your feature notes...'"
|
|
427
|
+
fi
|
|
428
|
+
fi
|
|
429
|
+
|
|
430
|
+
echo ""
|
|
431
|
+
|
|
432
|
+
# Show recent progress
|
|
433
|
+
if [[ -f "$RALPH_DIR/progress.txt" ]]; then
|
|
434
|
+
echo "Recent activity:"
|
|
435
|
+
tail -5 "$RALPH_DIR/progress.txt" | sed 's/^/ /'
|
|
436
|
+
fi
|
|
437
|
+
|
|
438
|
+
echo ""
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
# Show help
|
|
442
|
+
ralph_help() {
|
|
443
|
+
cat <<'EOF'
|
|
444
|
+
agentic-loop - Tools to thrive with agentic coding
|
|
445
|
+
|
|
446
|
+
What is this?
|
|
447
|
+
agentic-loop helps you build features using two terminals working together.
|
|
448
|
+
|
|
449
|
+
Terminal 1 - Claude Code (your AI pair programmer):
|
|
450
|
+
claude --dangerously-skip-permissions
|
|
451
|
+
|
|
452
|
+
The --dangerously-skip-permissions flag lets Claude edit files and run
|
|
453
|
+
commands without asking permission each time. This enables fluid,
|
|
454
|
+
uninterrupted collaboration while you brainstorm and refine ideas.
|
|
455
|
+
|
|
456
|
+
Use /idea to brainstorm big features. Claude saves your ideas to
|
|
457
|
+
docs/ideas/, then breaks them into small, executable PRDs.
|
|
458
|
+
|
|
459
|
+
Terminal 2 - Ralph (autonomous execution):
|
|
460
|
+
npx agentic-loop run
|
|
461
|
+
|
|
462
|
+
Ralph picks up the PRDs you created and builds them autonomously.
|
|
463
|
+
It writes code, runs tests, and iterates until each feature works.
|
|
464
|
+
Ralph runs separately so you can keep brainstorming in Claude.
|
|
465
|
+
|
|
466
|
+
Quick Start:
|
|
467
|
+
1. npx agentic-loop setup
|
|
468
|
+
2. Terminal 1: claude --dangerously-skip-permissions
|
|
469
|
+
3. In Claude: /idea "your feature description"
|
|
470
|
+
4. Terminal 2: npx agentic-loop run
|
|
471
|
+
5. Monitor Terminal 2 - it should be autonomous. If issues come up,
|
|
472
|
+
stop the loop (Ctrl+C) and paste the errors into Terminal 1.
|
|
473
|
+
|
|
474
|
+
Usage:
|
|
475
|
+
npx agentic-loop <command> [options]
|
|
476
|
+
|
|
477
|
+
Commands:
|
|
478
|
+
setup Set up project (hooks, config, CLAUDE.md)
|
|
479
|
+
init Initialize Ralph in current directory
|
|
480
|
+
config Re-detect and update project config
|
|
481
|
+
prd <notes> Generate PRD interactively (quick mode)
|
|
482
|
+
prd --file <file> Generate PRD from file
|
|
483
|
+
run Run autonomous loop until all stories pass
|
|
484
|
+
run --max <n> Run with max iterations (default: 20)
|
|
485
|
+
run --fast Skip code review for faster iterations
|
|
486
|
+
status Show current feature and story status
|
|
487
|
+
check Run verification checks only
|
|
488
|
+
verify <story-id> Verify a specific story
|
|
489
|
+
sign <pattern> [cat] Add a learned pattern (sign)
|
|
490
|
+
signs List all learned patterns
|
|
491
|
+
backup Backup detected databases to .backups/
|
|
492
|
+
backups List available database backups
|
|
493
|
+
restore <path> Restore database from backup
|
|
494
|
+
help Show this help message
|
|
495
|
+
|
|
496
|
+
PRD Generation:
|
|
497
|
+
/idea <description> Thorough brainstorm (in Claude Code)
|
|
498
|
+
npx agentic-loop prd <notes> Quick PRD generation
|
|
499
|
+
|
|
500
|
+
Examples:
|
|
501
|
+
npm install agentic-loop && npx agentic-loop setup
|
|
502
|
+
/idea "Add user authentication with OAuth"
|
|
503
|
+
npx agentic-loop prd "Add a contact form"
|
|
504
|
+
npx agentic-loop run
|
|
505
|
+
npx agentic-loop run --max 10
|
|
506
|
+
npx agentic-loop status
|
|
507
|
+
npx agentic-loop sign "Always use camelCase" frontend
|
|
508
|
+
|
|
509
|
+
Environment:
|
|
510
|
+
RALPH_DIR Override .ralph directory location (default: .ralph)
|
|
511
|
+
PROMPT_FILE Override PROMPT.md location (default: PROMPT.md)
|
|
512
|
+
|
|
513
|
+
For more information, see: https://github.com/allthriveai/agentic-loop
|
|
514
|
+
EOF
|
|
515
|
+
}
|