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/setup.sh
ADDED
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# shellcheck shell=bash
|
|
3
|
+
# setup.sh - Set up agentic-loop in a project
|
|
4
|
+
|
|
5
|
+
ralph_setup() {
|
|
6
|
+
echo ""
|
|
7
|
+
echo " _ _ _ _ "
|
|
8
|
+
echo " / \ __ _ ___ _ __ | |_(_) ___ | | ___ ___ _ __ "
|
|
9
|
+
echo " / _ \ / _\` |/ _ \ '_ \| __| |/ __| | | / _ \ / _ \| '_ \ "
|
|
10
|
+
echo " / ___ \ (_| | __/ | | | |_| | (__ | |__| (_) | (_) | |_) | "
|
|
11
|
+
echo "/_/ \_\__, |\___|_| |_|\__|_|\___| |_____\___/ \___/| .__/ "
|
|
12
|
+
echo " |___/ |_| "
|
|
13
|
+
echo ""
|
|
14
|
+
echo " Autonomous AI coding loop"
|
|
15
|
+
echo ""
|
|
16
|
+
|
|
17
|
+
# Package root is relative to this script (works with npx and local installs)
|
|
18
|
+
local pkg_root
|
|
19
|
+
pkg_root="$(cd "$RALPH_LIB/.." && pwd)"
|
|
20
|
+
|
|
21
|
+
# Run all setup steps
|
|
22
|
+
setup_ralph_dir "$pkg_root"
|
|
23
|
+
setup_gitignore
|
|
24
|
+
setup_claude_hooks "$pkg_root"
|
|
25
|
+
setup_slash_commands "$pkg_root"
|
|
26
|
+
setup_claude_md
|
|
27
|
+
setup_mcp
|
|
28
|
+
setup_precommit_hooks
|
|
29
|
+
|
|
30
|
+
echo ""
|
|
31
|
+
echo " ========================================"
|
|
32
|
+
echo " Setup complete!"
|
|
33
|
+
echo " ========================================"
|
|
34
|
+
echo ""
|
|
35
|
+
echo " Next steps (two terminals):"
|
|
36
|
+
echo ""
|
|
37
|
+
echo " --- Terminal 1: Claude Code ---"
|
|
38
|
+
echo " claude --dangerously-skip-permissions"
|
|
39
|
+
echo " /tour # Guided walkthrough"
|
|
40
|
+
echo " /idea 'your feature' # Generate a PRD"
|
|
41
|
+
echo ""
|
|
42
|
+
echo " --- Terminal 2: Ralph Loop ---"
|
|
43
|
+
echo " npx agentic-loop run # Execute PRDs autonomously"
|
|
44
|
+
echo " npx agentic-loop run --fast # Skip code review (~2x faster)"
|
|
45
|
+
echo ""
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# Create .ralph directory structure and config
|
|
49
|
+
setup_ralph_dir() {
|
|
50
|
+
local pkg_root="$1"
|
|
51
|
+
|
|
52
|
+
echo "Creating .ralph/ directory..."
|
|
53
|
+
mkdir -p ".ralph/archive" ".ralph/screenshots"
|
|
54
|
+
|
|
55
|
+
# Copy config template based on detected project type
|
|
56
|
+
if [[ ! -f ".ralph/config.json" ]]; then
|
|
57
|
+
local config_template=""
|
|
58
|
+
# Check for Go projects
|
|
59
|
+
if [[ -f "go.mod" ]]; then
|
|
60
|
+
config_template="$pkg_root/templates/config/go.json"
|
|
61
|
+
# Check for Rust projects
|
|
62
|
+
elif [[ -f "Cargo.toml" ]]; then
|
|
63
|
+
config_template="$pkg_root/templates/config/rust.json"
|
|
64
|
+
# Check for Hugo projects
|
|
65
|
+
elif [[ -f "hugo.toml" || -f "hugo.yaml" || -f "config.toml" ]] && [[ -d "content" || -d "layouts" || -d "themes" ]]; then
|
|
66
|
+
config_template="$pkg_root/templates/config/go.json"
|
|
67
|
+
# Check for Python projects
|
|
68
|
+
elif [[ -f "manage.py" ]] || [[ -f "pyproject.toml" ]]; then
|
|
69
|
+
config_template="$pkg_root/templates/config/python.json"
|
|
70
|
+
# Check for fullstack projects
|
|
71
|
+
elif [[ -d "frontend" ]] && [[ -d "backend" || -d "core" || -d "apps" ]]; then
|
|
72
|
+
config_template="$pkg_root/templates/config/fullstack.json"
|
|
73
|
+
# Check for Node projects
|
|
74
|
+
elif [[ -f "package.json" ]]; then
|
|
75
|
+
config_template="$pkg_root/templates/config/node.json"
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
if [[ -n "$config_template" ]] && [[ -f "$config_template" ]]; then
|
|
79
|
+
cp "$config_template" ".ralph/config.json"
|
|
80
|
+
else
|
|
81
|
+
echo '{"checks": {"build": true, "lint": true, "test": true}}' > ".ralph/config.json"
|
|
82
|
+
fi
|
|
83
|
+
echo " Created .ralph/config.json"
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
# Copy signs template
|
|
87
|
+
if [[ ! -f ".ralph/signs.json" ]]; then
|
|
88
|
+
if [[ -f "$pkg_root/templates/signs.json" ]]; then
|
|
89
|
+
cp "$pkg_root/templates/signs.json" ".ralph/signs.json"
|
|
90
|
+
else
|
|
91
|
+
echo '{"signs": []}' > ".ralph/signs.json"
|
|
92
|
+
fi
|
|
93
|
+
echo " Created .ralph/signs.json"
|
|
94
|
+
fi
|
|
95
|
+
|
|
96
|
+
# Create PROMPT.md if missing
|
|
97
|
+
if [[ ! -f "PROMPT.md" ]] && [[ -f "$pkg_root/templates/PROMPT.md" ]]; then
|
|
98
|
+
cp "$pkg_root/templates/PROMPT.md" "PROMPT.md"
|
|
99
|
+
echo " Created PROMPT.md"
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
# Auto-detect project settings
|
|
103
|
+
if command -v jq &>/dev/null; then
|
|
104
|
+
auto_configure_project
|
|
105
|
+
fi
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
# Ensure .gitignore has necessary patterns
|
|
109
|
+
setup_gitignore() {
|
|
110
|
+
local patterns=(
|
|
111
|
+
"node_modules/"
|
|
112
|
+
".env"
|
|
113
|
+
".env.local"
|
|
114
|
+
"*.log"
|
|
115
|
+
".DS_Store"
|
|
116
|
+
".ralph/last_*"
|
|
117
|
+
".ralph/screenshots/"
|
|
118
|
+
".ralph/archive/"
|
|
119
|
+
".backups/"
|
|
120
|
+
".claude/settings.json"
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
if [[ ! -f ".gitignore" ]]; then
|
|
124
|
+
printf '%s\n' "${patterns[@]}" > .gitignore
|
|
125
|
+
echo " Created .gitignore"
|
|
126
|
+
else
|
|
127
|
+
local added=false
|
|
128
|
+
for pattern in "${patterns[@]}"; do
|
|
129
|
+
if ! grep -qF "$pattern" .gitignore 2>/dev/null; then
|
|
130
|
+
echo "$pattern" >> .gitignore
|
|
131
|
+
added=true
|
|
132
|
+
fi
|
|
133
|
+
done
|
|
134
|
+
if [[ "$added" == "true" ]]; then
|
|
135
|
+
echo " Updated .gitignore"
|
|
136
|
+
fi
|
|
137
|
+
fi
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
# Install Claude Code hooks
|
|
141
|
+
setup_claude_hooks() {
|
|
142
|
+
local pkg_root="$1"
|
|
143
|
+
local settings_file=".claude/settings.json"
|
|
144
|
+
local src_hooks_dir="$pkg_root/ralph/hooks"
|
|
145
|
+
local project_hooks_dir=".ralph/hooks"
|
|
146
|
+
|
|
147
|
+
echo "Installing Claude Code hooks..."
|
|
148
|
+
|
|
149
|
+
# Check for source hooks directory
|
|
150
|
+
if [[ ! -d "$src_hooks_dir" ]]; then
|
|
151
|
+
print_warning "Hooks directory not found at $src_hooks_dir"
|
|
152
|
+
return 0
|
|
153
|
+
fi
|
|
154
|
+
|
|
155
|
+
# Check for jq
|
|
156
|
+
if ! command -v jq &>/dev/null; then
|
|
157
|
+
print_warning "jq not installed - skipping hooks setup"
|
|
158
|
+
echo " Install jq and run: npx agentic-loop setup"
|
|
159
|
+
return 0
|
|
160
|
+
fi
|
|
161
|
+
|
|
162
|
+
# Copy hooks into the project (so they survive package moves)
|
|
163
|
+
mkdir -p "$project_hooks_dir"
|
|
164
|
+
cp "$src_hooks_dir"/*.sh "$project_hooks_dir/" 2>/dev/null || true
|
|
165
|
+
chmod +x "$project_hooks_dir"/*.sh 2>/dev/null || true
|
|
166
|
+
echo " Copied hooks to $project_hooks_dir/"
|
|
167
|
+
|
|
168
|
+
# Get absolute path to project hooks
|
|
169
|
+
local hooks_dir
|
|
170
|
+
hooks_dir="$(cd "$project_hooks_dir" && pwd)"
|
|
171
|
+
|
|
172
|
+
# Create .claude directory
|
|
173
|
+
mkdir -p ".claude"
|
|
174
|
+
|
|
175
|
+
# Create settings file if it doesn't exist
|
|
176
|
+
[[ ! -f "$settings_file" ]] && echo '{}' > "$settings_file"
|
|
177
|
+
|
|
178
|
+
# Build hooks config with absolute paths to project hooks
|
|
179
|
+
local hooks_config
|
|
180
|
+
hooks_config=$(cat <<EOF
|
|
181
|
+
{
|
|
182
|
+
"PreToolUse": [
|
|
183
|
+
{
|
|
184
|
+
"matcher": "Edit|Write",
|
|
185
|
+
"hooks": [
|
|
186
|
+
{"type": "command", "command": "$hooks_dir/protect-prd.sh", "timeout": 5}
|
|
187
|
+
]
|
|
188
|
+
}
|
|
189
|
+
],
|
|
190
|
+
"PostToolUse": [
|
|
191
|
+
{
|
|
192
|
+
"matcher": "Edit|Write",
|
|
193
|
+
"hooks": [
|
|
194
|
+
{"type": "command", "command": "$hooks_dir/warn-debug.sh", "timeout": 5},
|
|
195
|
+
{"type": "command", "command": "$hooks_dir/warn-secrets.sh", "timeout": 5},
|
|
196
|
+
{"type": "command", "command": "$hooks_dir/warn-urls.sh", "timeout": 5},
|
|
197
|
+
{"type": "command", "command": "$hooks_dir/warn-empty-catch.sh", "timeout": 5}
|
|
198
|
+
]
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"matcher": "*",
|
|
202
|
+
"hooks": [
|
|
203
|
+
{"type": "command", "command": "$hooks_dir/log-tools.sh", "timeout": 3}
|
|
204
|
+
]
|
|
205
|
+
}
|
|
206
|
+
],
|
|
207
|
+
"SessionStart": [
|
|
208
|
+
{
|
|
209
|
+
"hooks": [
|
|
210
|
+
{"type": "command", "command": "$hooks_dir/inject-context.sh", "timeout": 5}
|
|
211
|
+
]
|
|
212
|
+
}
|
|
213
|
+
],
|
|
214
|
+
"Stop": [
|
|
215
|
+
{
|
|
216
|
+
"hooks": [
|
|
217
|
+
{"type": "command", "command": "$hooks_dir/save-learnings.sh", "timeout": 10}
|
|
218
|
+
]
|
|
219
|
+
}
|
|
220
|
+
]
|
|
221
|
+
}
|
|
222
|
+
EOF
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
# Merge hooks into settings
|
|
226
|
+
local tmp
|
|
227
|
+
tmp=$(mktemp)
|
|
228
|
+
jq --argjson hooks "$hooks_config" '.hooks = $hooks' "$settings_file" > "$tmp" && mv "$tmp" "$settings_file"
|
|
229
|
+
echo " Configured .claude/settings.json"
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
# Copy slash commands
|
|
233
|
+
setup_slash_commands() {
|
|
234
|
+
local pkg_root="$1"
|
|
235
|
+
|
|
236
|
+
if [[ -d "$pkg_root/.claude/commands" ]]; then
|
|
237
|
+
echo "Installing slash commands..."
|
|
238
|
+
mkdir -p .claude/commands
|
|
239
|
+
cp -r "$pkg_root/.claude/commands/"* .claude/commands/ 2>/dev/null || true
|
|
240
|
+
echo " Copied commands to .claude/commands/"
|
|
241
|
+
fi
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
# Generate CLAUDE.md with detected project info
|
|
245
|
+
setup_claude_md() {
|
|
246
|
+
local marker="<!-- agentic-loop-detected -->"
|
|
247
|
+
|
|
248
|
+
# Skip if we already added our section
|
|
249
|
+
[[ -f "CLAUDE.md" ]] && grep -q "$marker" "CLAUDE.md" 2>/dev/null && return 0
|
|
250
|
+
|
|
251
|
+
echo "Generating CLAUDE.md..."
|
|
252
|
+
|
|
253
|
+
local runtime="" framework="" language="" styling="" testing="" structure=""
|
|
254
|
+
|
|
255
|
+
# Detect runtime/language
|
|
256
|
+
local fe_dir=""
|
|
257
|
+
[[ -d "frontend" ]] && fe_dir="frontend"
|
|
258
|
+
[[ -d "client" ]] && fe_dir="client"
|
|
259
|
+
[[ -d "web" ]] && fe_dir="web"
|
|
260
|
+
[[ -d "apps/web" ]] && fe_dir="apps/web"
|
|
261
|
+
|
|
262
|
+
[[ -f "package.json" || -f "${fe_dir}/package.json" ]] && runtime="Node.js"
|
|
263
|
+
[[ -f "Cargo.toml" ]] && runtime="Rust"
|
|
264
|
+
[[ -f "go.mod" ]] && runtime="Go"
|
|
265
|
+
[[ -f "pyproject.toml" || -f "requirements.txt" || -f "manage.py" ]] && runtime="${runtime:+$runtime + }Python"
|
|
266
|
+
[[ -f "Gemfile" ]] && runtime="Ruby"
|
|
267
|
+
|
|
268
|
+
# Detect framework
|
|
269
|
+
local pkg="package.json"
|
|
270
|
+
[[ -n "$fe_dir" && -f "${fe_dir}/package.json" ]] && pkg="${fe_dir}/package.json"
|
|
271
|
+
|
|
272
|
+
if [[ -f "$pkg" ]]; then
|
|
273
|
+
grep -q '"next"' "$pkg" 2>/dev/null && framework="Next.js"
|
|
274
|
+
grep -q '"react"' "$pkg" 2>/dev/null && [[ -z "$framework" ]] && framework="React"
|
|
275
|
+
grep -q '"vue"' "$pkg" 2>/dev/null && framework="Vue"
|
|
276
|
+
grep -q '"svelte"' "$pkg" 2>/dev/null && framework="Svelte"
|
|
277
|
+
grep -q '"express"' "$pkg" 2>/dev/null && framework="${framework:+$framework + }Express"
|
|
278
|
+
fi
|
|
279
|
+
[[ -f "manage.py" ]] && framework="${framework:+$framework + }Django"
|
|
280
|
+
|
|
281
|
+
# Detect Hugo (Go static site generator)
|
|
282
|
+
for hugo_config in "hugo.toml" "hugo.yaml" "hugo.json" "config.toml"; do
|
|
283
|
+
if [[ -f "$hugo_config" ]] && [[ -d "content" || -d "layouts" || -d "themes" ]]; then
|
|
284
|
+
framework="${framework:+$framework + }Hugo"
|
|
285
|
+
[[ -z "$runtime" ]] && runtime="Go"
|
|
286
|
+
break
|
|
287
|
+
fi
|
|
288
|
+
done
|
|
289
|
+
|
|
290
|
+
# Detect TypeScript
|
|
291
|
+
[[ -f "tsconfig.json" || -f "${fe_dir}/tsconfig.json" ]] && language="TypeScript"
|
|
292
|
+
|
|
293
|
+
# Detect styling
|
|
294
|
+
[[ -f "tailwind.config.js" || -f "tailwind.config.ts" || -f "${fe_dir}/tailwind.config.js" || -f "${fe_dir}/tailwind.config.ts" ]] && styling="Tailwind CSS"
|
|
295
|
+
|
|
296
|
+
# Detect testing
|
|
297
|
+
[[ -f "vitest.config.ts" || -f "vitest.config.js" ]] && testing="Vitest"
|
|
298
|
+
[[ -f "jest.config.js" || -f "jest.config.ts" ]] && testing="Jest"
|
|
299
|
+
[[ -f "playwright.config.ts" || -f "playwright.config.js" ]] && testing="${testing:+$testing + }Playwright"
|
|
300
|
+
|
|
301
|
+
# Detect Python package manager
|
|
302
|
+
local python_runner=""
|
|
303
|
+
local api_dir=""
|
|
304
|
+
[[ -d "apps/api" ]] && api_dir="apps/api"
|
|
305
|
+
[[ -d "backend" ]] && api_dir="backend"
|
|
306
|
+
[[ -d "api" ]] && api_dir="api"
|
|
307
|
+
|
|
308
|
+
if [[ -f "uv.lock" ]] || [[ -n "$api_dir" && -f "$api_dir/uv.lock" ]]; then
|
|
309
|
+
python_runner="uv run python"
|
|
310
|
+
elif [[ -f "poetry.lock" ]] || [[ -n "$api_dir" && -f "$api_dir/poetry.lock" ]]; then
|
|
311
|
+
python_runner="poetry run python"
|
|
312
|
+
fi
|
|
313
|
+
|
|
314
|
+
# Build detected info section
|
|
315
|
+
local detected_section="
|
|
316
|
+
$marker
|
|
317
|
+
## Detected Project Info
|
|
318
|
+
|
|
319
|
+
${runtime:+- Runtime: $runtime}
|
|
320
|
+
${framework:+- Framework: $framework}
|
|
321
|
+
${language:+- Language: $language}
|
|
322
|
+
${styling:+- Styling: $styling}
|
|
323
|
+
${testing:+- Testing: $testing}
|
|
324
|
+
${python_runner:+- Python: Use \`$python_runner\` (not bare \`python\`)}
|
|
325
|
+
|
|
326
|
+
*Auto-detected by agentic-loop. Edit freely.*"
|
|
327
|
+
|
|
328
|
+
if [[ -f "CLAUDE.md" ]]; then
|
|
329
|
+
echo "$detected_section" >> CLAUDE.md
|
|
330
|
+
echo " Updated CLAUDE.md"
|
|
331
|
+
else
|
|
332
|
+
cat > CLAUDE.md << EOF
|
|
333
|
+
# Project Guide for Claude
|
|
334
|
+
|
|
335
|
+
## Your Rules
|
|
336
|
+
<!-- Add your project-specific rules, patterns, and conventions here -->
|
|
337
|
+
$detected_section
|
|
338
|
+
EOF
|
|
339
|
+
echo " Created CLAUDE.md"
|
|
340
|
+
fi
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
# Configure MCP (Chrome DevTools)
|
|
344
|
+
setup_mcp() {
|
|
345
|
+
local claude_json="$HOME/.claude.json"
|
|
346
|
+
|
|
347
|
+
# Skip if jq not available
|
|
348
|
+
command -v jq &>/dev/null || return 0
|
|
349
|
+
|
|
350
|
+
# Create claude.json if it doesn't exist
|
|
351
|
+
[[ ! -f "$claude_json" ]] && echo '{}' > "$claude_json"
|
|
352
|
+
|
|
353
|
+
# Skip if already configured
|
|
354
|
+
jq -e '.mcpServers["chrome-devtools"]' "$claude_json" > /dev/null 2>&1 && return 0
|
|
355
|
+
|
|
356
|
+
echo "Configuring MCP servers..."
|
|
357
|
+
local tmp
|
|
358
|
+
tmp=$(mktemp)
|
|
359
|
+
jq '.mcpServers["chrome-devtools"] = {
|
|
360
|
+
"command": "npx",
|
|
361
|
+
"args": ["-y", "@anthropic-ai/mcp-server-chrome-devtools@0.0.5"]
|
|
362
|
+
}' "$claude_json" > "$tmp" && mv "$tmp" "$claude_json"
|
|
363
|
+
echo " Added chrome-devtools MCP server"
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
# Set up pre-commit hooks
|
|
367
|
+
setup_precommit_hooks() {
|
|
368
|
+
echo "Setting up pre-commit hooks..."
|
|
369
|
+
|
|
370
|
+
# Create .pre-commit-config.yaml if it doesn't exist
|
|
371
|
+
if [[ ! -f ".pre-commit-config.yaml" ]]; then
|
|
372
|
+
cat > .pre-commit-config.yaml << 'EOF'
|
|
373
|
+
repos:
|
|
374
|
+
- repo: https://github.com/allthriveai/agentic-loop
|
|
375
|
+
rev: v1.0.0
|
|
376
|
+
hooks:
|
|
377
|
+
- id: check-secrets
|
|
378
|
+
name: Check for hardcoded secrets
|
|
379
|
+
- id: check-hardcoded-urls
|
|
380
|
+
name: Check for hardcoded URLs
|
|
381
|
+
- id: check-debug
|
|
382
|
+
name: Check for debug statements
|
|
383
|
+
EOF
|
|
384
|
+
echo " Created .pre-commit-config.yaml"
|
|
385
|
+
else
|
|
386
|
+
echo " Skipped: .pre-commit-config.yaml already exists"
|
|
387
|
+
fi
|
|
388
|
+
|
|
389
|
+
# Install hooks if pre-commit is available
|
|
390
|
+
if command -v pre-commit &>/dev/null; then
|
|
391
|
+
if [[ -d ".git" ]]; then
|
|
392
|
+
pre-commit install > /dev/null 2>&1
|
|
393
|
+
echo " Hooks installed"
|
|
394
|
+
else
|
|
395
|
+
echo " Not a git repo - run 'pre-commit install' after git init"
|
|
396
|
+
fi
|
|
397
|
+
else
|
|
398
|
+
echo " pre-commit not found - install with: pip install pre-commit"
|
|
399
|
+
echo " Then run: pre-commit install"
|
|
400
|
+
fi
|
|
401
|
+
}
|
package/ralph/signs.sh
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# shellcheck shell=bash
|
|
3
|
+
# signs.sh - Learned patterns management
|
|
4
|
+
|
|
5
|
+
# Add a new sign (learned pattern)
|
|
6
|
+
ralph_sign() {
|
|
7
|
+
if [[ $# -lt 1 ]]; then
|
|
8
|
+
print_error "Usage: ralph sign 'pattern' [category]"
|
|
9
|
+
echo ""
|
|
10
|
+
echo "Examples:"
|
|
11
|
+
echo " ralph sign 'Always use camelCase in WebSocket responses' frontend"
|
|
12
|
+
echo " ralph sign 'Run migrations before seeding' backend"
|
|
13
|
+
echo " ralph sign 'Check for null before accessing nested props' general"
|
|
14
|
+
return 1
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
local pattern="$1"
|
|
18
|
+
local category="${2:-general}"
|
|
19
|
+
|
|
20
|
+
# Ensure .ralph directory exists
|
|
21
|
+
if [[ ! -d "$RALPH_DIR" ]]; then
|
|
22
|
+
print_error "Ralph not initialized. Run 'ralph init' first."
|
|
23
|
+
return 1
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
# Ensure signs.json exists
|
|
27
|
+
if [[ ! -f "$RALPH_DIR/signs.json" ]]; then
|
|
28
|
+
echo '{"signs": []}' > "$RALPH_DIR/signs.json"
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
# Generate sign ID
|
|
32
|
+
local sign_count
|
|
33
|
+
sign_count=$(jq '.signs | length' "$RALPH_DIR/signs.json")
|
|
34
|
+
local sign_id="sign-$(printf '%03d' $((sign_count + 1)))"
|
|
35
|
+
|
|
36
|
+
# Get current story if available (for learnedFrom field)
|
|
37
|
+
local learned_from=""
|
|
38
|
+
if [[ -f "$RALPH_DIR/prd.json" ]]; then
|
|
39
|
+
learned_from=$(jq -r '.stories[] | select(.passes==false) | .id' "$RALPH_DIR/prd.json" 2>/dev/null | head -1)
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
# Get timestamp
|
|
43
|
+
local timestamp
|
|
44
|
+
timestamp=$(date -Iseconds 2>/dev/null || date +%Y-%m-%dT%H:%M:%S)
|
|
45
|
+
|
|
46
|
+
# Add the sign
|
|
47
|
+
local tmpfile
|
|
48
|
+
tmpfile=$(mktemp)
|
|
49
|
+
|
|
50
|
+
if jq --arg id "$sign_id" \
|
|
51
|
+
--arg pattern "$pattern" \
|
|
52
|
+
--arg category "$category" \
|
|
53
|
+
--arg learnedFrom "$learned_from" \
|
|
54
|
+
--arg createdAt "$timestamp" \
|
|
55
|
+
'.signs += [{
|
|
56
|
+
id: $id,
|
|
57
|
+
pattern: $pattern,
|
|
58
|
+
category: $category,
|
|
59
|
+
learnedFrom: (if $learnedFrom == "" then null else $learnedFrom end),
|
|
60
|
+
createdAt: $createdAt
|
|
61
|
+
}]' "$RALPH_DIR/signs.json" > "$tmpfile" && jq -e . "$tmpfile" >/dev/null 2>&1; then
|
|
62
|
+
mv "$tmpfile" "$RALPH_DIR/signs.json"
|
|
63
|
+
print_success "Added sign: [$category] $pattern"
|
|
64
|
+
log_progress "SIGN" "ADDED" "$sign_id: $pattern"
|
|
65
|
+
else
|
|
66
|
+
rm -f "$tmpfile"
|
|
67
|
+
print_error "Failed to add sign"
|
|
68
|
+
return 1
|
|
69
|
+
fi
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
# List all signs
|
|
73
|
+
ralph_signs() {
|
|
74
|
+
if [[ ! -f "$RALPH_DIR/signs.json" ]]; then
|
|
75
|
+
echo "No signs recorded yet."
|
|
76
|
+
echo ""
|
|
77
|
+
echo "Add one with: ralph sign 'your learned pattern' [category]"
|
|
78
|
+
return 0
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
local count
|
|
82
|
+
count=$(jq '.signs | length' "$RALPH_DIR/signs.json")
|
|
83
|
+
|
|
84
|
+
if [[ "$count" -eq 0 ]]; then
|
|
85
|
+
echo "No signs recorded yet."
|
|
86
|
+
echo ""
|
|
87
|
+
echo "Add one with: ralph sign 'your learned pattern' [category]"
|
|
88
|
+
return 0
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
echo ""
|
|
92
|
+
print_info "=== Learned Patterns ($count signs) ==="
|
|
93
|
+
echo ""
|
|
94
|
+
|
|
95
|
+
# Group by category
|
|
96
|
+
local categories
|
|
97
|
+
categories=$(jq -r '.signs[].category' "$RALPH_DIR/signs.json" | sort -u)
|
|
98
|
+
|
|
99
|
+
while IFS= read -r category; do
|
|
100
|
+
[[ -z "$category" ]] && continue
|
|
101
|
+
|
|
102
|
+
echo "[$category]"
|
|
103
|
+
jq -r --arg cat "$category" '.signs[] | select(.category==$cat) | " - \(.pattern)"' "$RALPH_DIR/signs.json"
|
|
104
|
+
echo ""
|
|
105
|
+
done <<< "$categories"
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
# Remove a sign by ID or pattern match
|
|
109
|
+
ralph_unsign() {
|
|
110
|
+
local target="$1"
|
|
111
|
+
|
|
112
|
+
if [[ -z "$target" ]]; then
|
|
113
|
+
print_error "Usage: ralph unsign <sign-id or pattern>"
|
|
114
|
+
return 1
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
if [[ ! -f "$RALPH_DIR/signs.json" ]]; then
|
|
118
|
+
print_error "No signs file found"
|
|
119
|
+
return 1
|
|
120
|
+
fi
|
|
121
|
+
|
|
122
|
+
local tmpfile
|
|
123
|
+
tmpfile=$(mktemp)
|
|
124
|
+
|
|
125
|
+
# Try to match by ID first, then by pattern substring
|
|
126
|
+
if jq --arg target "$target" '
|
|
127
|
+
if (.signs | map(select(.id == $target)) | length) > 0 then
|
|
128
|
+
.signs |= map(select(.id != $target))
|
|
129
|
+
else
|
|
130
|
+
.signs |= map(select(.pattern | contains($target) | not))
|
|
131
|
+
end
|
|
132
|
+
' "$RALPH_DIR/signs.json" > "$tmpfile"; then
|
|
133
|
+
local before after removed
|
|
134
|
+
before=$(jq '.signs | length' "$RALPH_DIR/signs.json")
|
|
135
|
+
after=$(jq '.signs | length' "$tmpfile")
|
|
136
|
+
removed=$((before - after))
|
|
137
|
+
|
|
138
|
+
if [[ $removed -gt 0 ]]; then
|
|
139
|
+
mv "$tmpfile" "$RALPH_DIR/signs.json"
|
|
140
|
+
print_success "Removed $removed sign(s)"
|
|
141
|
+
else
|
|
142
|
+
rm -f "$tmpfile"
|
|
143
|
+
print_warning "No matching signs found"
|
|
144
|
+
fi
|
|
145
|
+
else
|
|
146
|
+
rm -f "$tmpfile"
|
|
147
|
+
print_error "Failed to process signs"
|
|
148
|
+
return 1
|
|
149
|
+
fi
|
|
150
|
+
}
|