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.
Files changed (162) hide show
  1. package/.claude/commands/explain.md +114 -0
  2. package/.claude/commands/idea.md +398 -0
  3. package/.claude/commands/my-dna.md +122 -0
  4. package/.claude/commands/prd.md +286 -0
  5. package/.claude/commands/review.md +167 -0
  6. package/.claude/commands/sign.md +32 -0
  7. package/.claude/commands/styleguide.md +450 -0
  8. package/.claude/commands/tour.md +301 -0
  9. package/.claude/commands/vibe-check.md +116 -0
  10. package/.claude/commands/vibe-help.md +47 -0
  11. package/.claude/commands/vibe-list.md +203 -0
  12. package/.pre-commit-hooks.yaml +102 -0
  13. package/LICENSE +21 -0
  14. package/README.md +238 -0
  15. package/bin/agentic-loop.sh +24 -0
  16. package/bin/postinstall.sh +29 -0
  17. package/bin/ralph.sh +171 -0
  18. package/bin/vibe-check.js +19 -0
  19. package/dist/checks/check-any-types.d.ts +6 -0
  20. package/dist/checks/check-any-types.d.ts.map +1 -0
  21. package/dist/checks/check-any-types.js +73 -0
  22. package/dist/checks/check-any-types.js.map +1 -0
  23. package/dist/checks/check-commented-code.d.ts +6 -0
  24. package/dist/checks/check-commented-code.d.ts.map +1 -0
  25. package/dist/checks/check-commented-code.js +81 -0
  26. package/dist/checks/check-commented-code.js.map +1 -0
  27. package/dist/checks/check-console-error.d.ts +6 -0
  28. package/dist/checks/check-console-error.d.ts.map +1 -0
  29. package/dist/checks/check-console-error.js +41 -0
  30. package/dist/checks/check-console-error.js.map +1 -0
  31. package/dist/checks/check-debug-statements.d.ts +6 -0
  32. package/dist/checks/check-debug-statements.d.ts.map +1 -0
  33. package/dist/checks/check-debug-statements.js +120 -0
  34. package/dist/checks/check-debug-statements.js.map +1 -0
  35. package/dist/checks/check-deep-nesting.d.ts +6 -0
  36. package/dist/checks/check-deep-nesting.d.ts.map +1 -0
  37. package/dist/checks/check-deep-nesting.js +116 -0
  38. package/dist/checks/check-deep-nesting.js.map +1 -0
  39. package/dist/checks/check-docker-platform.d.ts +6 -0
  40. package/dist/checks/check-docker-platform.d.ts.map +1 -0
  41. package/dist/checks/check-docker-platform.js +42 -0
  42. package/dist/checks/check-docker-platform.js.map +1 -0
  43. package/dist/checks/check-dry-violations.d.ts +6 -0
  44. package/dist/checks/check-dry-violations.d.ts.map +1 -0
  45. package/dist/checks/check-dry-violations.js +124 -0
  46. package/dist/checks/check-dry-violations.js.map +1 -0
  47. package/dist/checks/check-empty-catch.d.ts +6 -0
  48. package/dist/checks/check-empty-catch.d.ts.map +1 -0
  49. package/dist/checks/check-empty-catch.js +111 -0
  50. package/dist/checks/check-empty-catch.js.map +1 -0
  51. package/dist/checks/check-function-length.d.ts +6 -0
  52. package/dist/checks/check-function-length.d.ts.map +1 -0
  53. package/dist/checks/check-function-length.js +152 -0
  54. package/dist/checks/check-function-length.js.map +1 -0
  55. package/dist/checks/check-hardcoded-ai-models.d.ts +10 -0
  56. package/dist/checks/check-hardcoded-ai-models.d.ts.map +1 -0
  57. package/dist/checks/check-hardcoded-ai-models.js +102 -0
  58. package/dist/checks/check-hardcoded-ai-models.js.map +1 -0
  59. package/dist/checks/check-hardcoded-urls.d.ts +6 -0
  60. package/dist/checks/check-hardcoded-urls.d.ts.map +1 -0
  61. package/dist/checks/check-hardcoded-urls.js +124 -0
  62. package/dist/checks/check-hardcoded-urls.js.map +1 -0
  63. package/dist/checks/check-magic-numbers.d.ts +6 -0
  64. package/dist/checks/check-magic-numbers.d.ts.map +1 -0
  65. package/dist/checks/check-magic-numbers.js +116 -0
  66. package/dist/checks/check-magic-numbers.js.map +1 -0
  67. package/dist/checks/check-secrets.d.ts +6 -0
  68. package/dist/checks/check-secrets.d.ts.map +1 -0
  69. package/dist/checks/check-secrets.js +138 -0
  70. package/dist/checks/check-secrets.js.map +1 -0
  71. package/dist/checks/check-snake-case-ts.d.ts +6 -0
  72. package/dist/checks/check-snake-case-ts.d.ts.map +1 -0
  73. package/dist/checks/check-snake-case-ts.js +78 -0
  74. package/dist/checks/check-snake-case-ts.js.map +1 -0
  75. package/dist/checks/check-todo-fixme.d.ts +6 -0
  76. package/dist/checks/check-todo-fixme.d.ts.map +1 -0
  77. package/dist/checks/check-todo-fixme.js +41 -0
  78. package/dist/checks/check-todo-fixme.js.map +1 -0
  79. package/dist/checks/check-unsafe-html.d.ts +6 -0
  80. package/dist/checks/check-unsafe-html.d.ts.map +1 -0
  81. package/dist/checks/check-unsafe-html.js +101 -0
  82. package/dist/checks/check-unsafe-html.js.map +1 -0
  83. package/dist/checks/index.d.ts +30 -0
  84. package/dist/checks/index.d.ts.map +1 -0
  85. package/dist/checks/index.js +57 -0
  86. package/dist/checks/index.js.map +1 -0
  87. package/dist/cli.d.ts +13 -0
  88. package/dist/cli.d.ts.map +1 -0
  89. package/dist/cli.js +208 -0
  90. package/dist/cli.js.map +1 -0
  91. package/dist/index.d.ts +9 -0
  92. package/dist/index.d.ts.map +1 -0
  93. package/dist/index.js +10 -0
  94. package/dist/index.js.map +1 -0
  95. package/dist/utils/file-reader.d.ts +24 -0
  96. package/dist/utils/file-reader.d.ts.map +1 -0
  97. package/dist/utils/file-reader.js +146 -0
  98. package/dist/utils/file-reader.js.map +1 -0
  99. package/dist/utils/patterns.d.ts +27 -0
  100. package/dist/utils/patterns.d.ts.map +1 -0
  101. package/dist/utils/patterns.js +84 -0
  102. package/dist/utils/patterns.js.map +1 -0
  103. package/dist/utils/reporters.d.ts +21 -0
  104. package/dist/utils/reporters.d.ts.map +1 -0
  105. package/dist/utils/reporters.js +115 -0
  106. package/dist/utils/reporters.js.map +1 -0
  107. package/dist/utils/types.d.ts +71 -0
  108. package/dist/utils/types.d.ts.map +1 -0
  109. package/dist/utils/types.js +5 -0
  110. package/dist/utils/types.js.map +1 -0
  111. package/package.json +83 -0
  112. package/ralph/api.sh +216 -0
  113. package/ralph/backup.sh +838 -0
  114. package/ralph/browser-verify/README.md +135 -0
  115. package/ralph/browser-verify/verify.ts +450 -0
  116. package/ralph/checks/check-fastapi-responses.py +155 -0
  117. package/ralph/hooks/hooks-config.json +72 -0
  118. package/ralph/hooks/inject-context.sh +44 -0
  119. package/ralph/hooks/install.sh +207 -0
  120. package/ralph/hooks/log-tools.sh +45 -0
  121. package/ralph/hooks/protect-prd.sh +27 -0
  122. package/ralph/hooks/save-learnings.sh +36 -0
  123. package/ralph/hooks/warn-debug.sh +54 -0
  124. package/ralph/hooks/warn-empty-catch.sh +63 -0
  125. package/ralph/hooks/warn-secrets.sh +89 -0
  126. package/ralph/hooks/warn-urls.sh +77 -0
  127. package/ralph/init.sh +515 -0
  128. package/ralph/loop.sh +730 -0
  129. package/ralph/playwright.sh +238 -0
  130. package/ralph/prd.sh +295 -0
  131. package/ralph/setup/feature-tour.sh +155 -0
  132. package/ralph/setup/quick-setup.sh +239 -0
  133. package/ralph/setup/tutorial.sh +159 -0
  134. package/ralph/setup/ui.sh +136 -0
  135. package/ralph/setup.sh +401 -0
  136. package/ralph/signs.sh +150 -0
  137. package/ralph/utils.sh +682 -0
  138. package/ralph/verify/browser.sh +324 -0
  139. package/ralph/verify/lint.sh +363 -0
  140. package/ralph/verify/review.sh +152 -0
  141. package/ralph/verify/tests.sh +81 -0
  142. package/ralph/verify.sh +268 -0
  143. package/templates/PROMPT.md +235 -0
  144. package/templates/config/fullstack.json +86 -0
  145. package/templates/config/go.json +81 -0
  146. package/templates/config/minimal.json +76 -0
  147. package/templates/config/node.json +81 -0
  148. package/templates/config/python.json +81 -0
  149. package/templates/config/rust.json +81 -0
  150. package/templates/examples/CLAUDE-django.md +174 -0
  151. package/templates/examples/CLAUDE-fastapi.md +270 -0
  152. package/templates/examples/CLAUDE-fastmcp.md +352 -0
  153. package/templates/examples/CLAUDE-fullstack.md +256 -0
  154. package/templates/examples/CLAUDE-node.md +246 -0
  155. package/templates/examples/CLAUDE-react.md +138 -0
  156. package/templates/optional/cursorrules.template +147 -0
  157. package/templates/optional/eslint.config.js +34 -0
  158. package/templates/optional/lint-staged.config.js +34 -0
  159. package/templates/optional/ruff.toml +125 -0
  160. package/templates/optional/vibe-check.yml +116 -0
  161. package/templates/optional/vscode-settings.json +127 -0
  162. 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
+ }