thrivekit 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/.claude/commands/explain.md +114 -0
  2. package/.claude/commands/idea.md +370 -0
  3. package/.claude/commands/my-dna.md +122 -0
  4. package/.claude/commands/prd.md +286 -0
  5. package/.claude/commands/review.md +167 -0
  6. package/.claude/commands/sign.md +32 -0
  7. package/.claude/commands/styleguide.md +450 -0
  8. package/.claude/commands/tour.md +301 -0
  9. package/.claude/commands/vibe-check.md +116 -0
  10. package/.claude/commands/vibe-help.md +47 -0
  11. package/.claude/commands/vibe-list.md +203 -0
  12. package/.claude/settings.json +75 -0
  13. package/.claude/settings.local.json +12 -0
  14. package/.pre-commit-hooks.yaml +102 -0
  15. package/LICENSE +21 -0
  16. package/README.md +214 -0
  17. package/bin/postinstall.sh +29 -0
  18. package/bin/ralph.sh +171 -0
  19. package/bin/thrivekit.sh +24 -0
  20. package/bin/vibe-check.js +19 -0
  21. package/dist/checks/check-any-types.d.ts +6 -0
  22. package/dist/checks/check-any-types.d.ts.map +1 -0
  23. package/dist/checks/check-any-types.js +73 -0
  24. package/dist/checks/check-any-types.js.map +1 -0
  25. package/dist/checks/check-commented-code.d.ts +6 -0
  26. package/dist/checks/check-commented-code.d.ts.map +1 -0
  27. package/dist/checks/check-commented-code.js +81 -0
  28. package/dist/checks/check-commented-code.js.map +1 -0
  29. package/dist/checks/check-console-error.d.ts +6 -0
  30. package/dist/checks/check-console-error.d.ts.map +1 -0
  31. package/dist/checks/check-console-error.js +41 -0
  32. package/dist/checks/check-console-error.js.map +1 -0
  33. package/dist/checks/check-debug-statements.d.ts +6 -0
  34. package/dist/checks/check-debug-statements.d.ts.map +1 -0
  35. package/dist/checks/check-debug-statements.js +120 -0
  36. package/dist/checks/check-debug-statements.js.map +1 -0
  37. package/dist/checks/check-deep-nesting.d.ts +6 -0
  38. package/dist/checks/check-deep-nesting.d.ts.map +1 -0
  39. package/dist/checks/check-deep-nesting.js +116 -0
  40. package/dist/checks/check-deep-nesting.js.map +1 -0
  41. package/dist/checks/check-docker-platform.d.ts +6 -0
  42. package/dist/checks/check-docker-platform.d.ts.map +1 -0
  43. package/dist/checks/check-docker-platform.js +42 -0
  44. package/dist/checks/check-docker-platform.js.map +1 -0
  45. package/dist/checks/check-dry-violations.d.ts +6 -0
  46. package/dist/checks/check-dry-violations.d.ts.map +1 -0
  47. package/dist/checks/check-dry-violations.js +124 -0
  48. package/dist/checks/check-dry-violations.js.map +1 -0
  49. package/dist/checks/check-empty-catch.d.ts +6 -0
  50. package/dist/checks/check-empty-catch.d.ts.map +1 -0
  51. package/dist/checks/check-empty-catch.js +111 -0
  52. package/dist/checks/check-empty-catch.js.map +1 -0
  53. package/dist/checks/check-function-length.d.ts +6 -0
  54. package/dist/checks/check-function-length.d.ts.map +1 -0
  55. package/dist/checks/check-function-length.js +152 -0
  56. package/dist/checks/check-function-length.js.map +1 -0
  57. package/dist/checks/check-hardcoded-ai-models.d.ts +10 -0
  58. package/dist/checks/check-hardcoded-ai-models.d.ts.map +1 -0
  59. package/dist/checks/check-hardcoded-ai-models.js +102 -0
  60. package/dist/checks/check-hardcoded-ai-models.js.map +1 -0
  61. package/dist/checks/check-hardcoded-urls.d.ts +6 -0
  62. package/dist/checks/check-hardcoded-urls.d.ts.map +1 -0
  63. package/dist/checks/check-hardcoded-urls.js +124 -0
  64. package/dist/checks/check-hardcoded-urls.js.map +1 -0
  65. package/dist/checks/check-magic-numbers.d.ts +6 -0
  66. package/dist/checks/check-magic-numbers.d.ts.map +1 -0
  67. package/dist/checks/check-magic-numbers.js +116 -0
  68. package/dist/checks/check-magic-numbers.js.map +1 -0
  69. package/dist/checks/check-secrets.d.ts +6 -0
  70. package/dist/checks/check-secrets.d.ts.map +1 -0
  71. package/dist/checks/check-secrets.js +138 -0
  72. package/dist/checks/check-secrets.js.map +1 -0
  73. package/dist/checks/check-snake-case-ts.d.ts +6 -0
  74. package/dist/checks/check-snake-case-ts.d.ts.map +1 -0
  75. package/dist/checks/check-snake-case-ts.js +78 -0
  76. package/dist/checks/check-snake-case-ts.js.map +1 -0
  77. package/dist/checks/check-todo-fixme.d.ts +6 -0
  78. package/dist/checks/check-todo-fixme.d.ts.map +1 -0
  79. package/dist/checks/check-todo-fixme.js +41 -0
  80. package/dist/checks/check-todo-fixme.js.map +1 -0
  81. package/dist/checks/check-unsafe-html.d.ts +6 -0
  82. package/dist/checks/check-unsafe-html.d.ts.map +1 -0
  83. package/dist/checks/check-unsafe-html.js +101 -0
  84. package/dist/checks/check-unsafe-html.js.map +1 -0
  85. package/dist/checks/index.d.ts +30 -0
  86. package/dist/checks/index.d.ts.map +1 -0
  87. package/dist/checks/index.js +57 -0
  88. package/dist/checks/index.js.map +1 -0
  89. package/dist/cli.d.ts +13 -0
  90. package/dist/cli.d.ts.map +1 -0
  91. package/dist/cli.js +206 -0
  92. package/dist/cli.js.map +1 -0
  93. package/dist/index.d.ts +9 -0
  94. package/dist/index.d.ts.map +1 -0
  95. package/dist/index.js +10 -0
  96. package/dist/index.js.map +1 -0
  97. package/dist/utils/file-reader.d.ts +24 -0
  98. package/dist/utils/file-reader.d.ts.map +1 -0
  99. package/dist/utils/file-reader.js +140 -0
  100. package/dist/utils/file-reader.js.map +1 -0
  101. package/dist/utils/patterns.d.ts +27 -0
  102. package/dist/utils/patterns.d.ts.map +1 -0
  103. package/dist/utils/patterns.js +84 -0
  104. package/dist/utils/patterns.js.map +1 -0
  105. package/dist/utils/reporters.d.ts +21 -0
  106. package/dist/utils/reporters.d.ts.map +1 -0
  107. package/dist/utils/reporters.js +115 -0
  108. package/dist/utils/reporters.js.map +1 -0
  109. package/dist/utils/types.d.ts +71 -0
  110. package/dist/utils/types.d.ts.map +1 -0
  111. package/dist/utils/types.js +5 -0
  112. package/dist/utils/types.js.map +1 -0
  113. package/package.json +82 -0
  114. package/ralph/api.sh +210 -0
  115. package/ralph/backup.sh +838 -0
  116. package/ralph/browser-verify/README.md +135 -0
  117. package/ralph/browser-verify/verify.ts +450 -0
  118. package/ralph/checks/check-fastapi-responses.py +155 -0
  119. package/ralph/hooks/hooks-config.json +72 -0
  120. package/ralph/hooks/inject-context.sh +44 -0
  121. package/ralph/hooks/install.sh +207 -0
  122. package/ralph/hooks/log-tools.sh +45 -0
  123. package/ralph/hooks/protect-prd.sh +27 -0
  124. package/ralph/hooks/save-learnings.sh +36 -0
  125. package/ralph/hooks/warn-debug.sh +54 -0
  126. package/ralph/hooks/warn-empty-catch.sh +63 -0
  127. package/ralph/hooks/warn-secrets.sh +89 -0
  128. package/ralph/hooks/warn-urls.sh +77 -0
  129. package/ralph/init.sh +388 -0
  130. package/ralph/loop.sh +570 -0
  131. package/ralph/playwright.sh +238 -0
  132. package/ralph/prd.sh +295 -0
  133. package/ralph/setup/feature-tour.sh +155 -0
  134. package/ralph/setup/quick-setup.sh +239 -0
  135. package/ralph/setup/tutorial.sh +159 -0
  136. package/ralph/setup/ui.sh +136 -0
  137. package/ralph/setup.sh +353 -0
  138. package/ralph/signs.sh +150 -0
  139. package/ralph/utils.sh +682 -0
  140. package/ralph/verify/browser.sh +324 -0
  141. package/ralph/verify/lint.sh +363 -0
  142. package/ralph/verify/review.sh +164 -0
  143. package/ralph/verify/tests.sh +81 -0
  144. package/ralph/verify.sh +224 -0
  145. package/templates/PROMPT.md +235 -0
  146. package/templates/config/fullstack.json +86 -0
  147. package/templates/config/go.json +81 -0
  148. package/templates/config/minimal.json +76 -0
  149. package/templates/config/node.json +81 -0
  150. package/templates/config/python.json +81 -0
  151. package/templates/config/rust.json +81 -0
  152. package/templates/examples/CLAUDE-django.md +174 -0
  153. package/templates/examples/CLAUDE-fastapi.md +270 -0
  154. package/templates/examples/CLAUDE-fastmcp.md +352 -0
  155. package/templates/examples/CLAUDE-fullstack.md +256 -0
  156. package/templates/examples/CLAUDE-node.md +246 -0
  157. package/templates/examples/CLAUDE-react.md +138 -0
  158. package/templates/optional/cursorrules.template +147 -0
  159. package/templates/optional/eslint.config.js +34 -0
  160. package/templates/optional/lint-staged.config.js +34 -0
  161. package/templates/optional/ruff.toml +125 -0
  162. package/templates/optional/vibe-check.yml +116 -0
  163. package/templates/optional/vscode-settings.json +127 -0
  164. package/templates/signs.json +46 -0
package/ralph/setup.sh ADDED
@@ -0,0 +1,353 @@
1
+ #!/usr/bin/env bash
2
+ # shellcheck shell=bash
3
+ # setup.sh - Set up thrivekit in a project
4
+
5
+ ralph_setup() {
6
+ echo ""
7
+ echo "Setting up thrivekit..."
8
+ echo ""
9
+
10
+ # Find the package root (where thrivekit is installed)
11
+ local pkg_root=""
12
+
13
+ # Check common locations
14
+ if [[ -d "node_modules/thrivekit" ]]; then
15
+ pkg_root="node_modules/thrivekit"
16
+ elif [[ -d "apps/web/node_modules/thrivekit" ]]; then
17
+ pkg_root="apps/web/node_modules/thrivekit"
18
+ elif [[ -d "apps/frontend/node_modules/thrivekit" ]]; then
19
+ pkg_root="apps/frontend/node_modules/thrivekit"
20
+ elif [[ -d "frontend/node_modules/thrivekit" ]]; then
21
+ pkg_root="frontend/node_modules/thrivekit"
22
+ else
23
+ # Search for it
24
+ pkg_root=$(find . -path "*/node_modules/thrivekit" -type d 2>/dev/null | head -1)
25
+ fi
26
+
27
+ if [[ -z "$pkg_root" || ! -d "$pkg_root" ]]; then
28
+ print_error "thrivekit not found in node_modules."
29
+ echo "Run: npm install thrivekit"
30
+ return 1
31
+ fi
32
+
33
+ # Convert to absolute path
34
+ pkg_root="$(cd "$pkg_root" && pwd)"
35
+ echo "Found package at: $pkg_root"
36
+ echo ""
37
+
38
+ # Run all setup steps
39
+ setup_ralph_dir "$pkg_root"
40
+ setup_gitignore
41
+ setup_claude_hooks "$pkg_root"
42
+ setup_slash_commands "$pkg_root"
43
+ setup_claude_md
44
+ setup_mcp
45
+
46
+ echo ""
47
+ print_success "Setup complete!"
48
+ echo ""
49
+ echo "Next steps:"
50
+ echo " claude # Start Claude Code"
51
+ echo " /idea 'your feature' # Generate a PRD"
52
+ echo " npx thrivekit run # Execute autonomously"
53
+ echo ""
54
+ echo "Or type /tour for a guided walkthrough."
55
+ echo ""
56
+ }
57
+
58
+ # Create .ralph directory structure and config
59
+ setup_ralph_dir() {
60
+ local pkg_root="$1"
61
+
62
+ echo "Creating .ralph/ directory..."
63
+ mkdir -p ".ralph/archive" ".ralph/screenshots"
64
+
65
+ # Copy config template based on detected project type
66
+ if [[ ! -f ".ralph/config.json" ]]; then
67
+ local config_template=""
68
+ if [[ -f "manage.py" ]] || [[ -f "pyproject.toml" ]]; then
69
+ config_template="$pkg_root/templates/config/python.json"
70
+ elif [[ -d "frontend" ]] && [[ -d "backend" || -d "core" || -d "apps" ]]; then
71
+ config_template="$pkg_root/templates/config/fullstack.json"
72
+ elif [[ -f "package.json" ]]; then
73
+ config_template="$pkg_root/templates/config/node.json"
74
+ fi
75
+
76
+ if [[ -n "$config_template" ]] && [[ -f "$config_template" ]]; then
77
+ cp "$config_template" ".ralph/config.json"
78
+ else
79
+ echo '{"checks": {"build": true, "lint": true, "test": true}}' > ".ralph/config.json"
80
+ fi
81
+ echo " Created .ralph/config.json"
82
+ fi
83
+
84
+ # Copy signs template
85
+ if [[ ! -f ".ralph/signs.json" ]]; then
86
+ if [[ -f "$pkg_root/templates/signs.json" ]]; then
87
+ cp "$pkg_root/templates/signs.json" ".ralph/signs.json"
88
+ else
89
+ echo '{"signs": []}' > ".ralph/signs.json"
90
+ fi
91
+ echo " Created .ralph/signs.json"
92
+ fi
93
+
94
+ # Create PROMPT.md if missing
95
+ if [[ ! -f "PROMPT.md" ]] && [[ -f "$pkg_root/templates/PROMPT.md" ]]; then
96
+ cp "$pkg_root/templates/PROMPT.md" "PROMPT.md"
97
+ echo " Created PROMPT.md"
98
+ fi
99
+
100
+ # Auto-detect project settings
101
+ if command -v jq &>/dev/null; then
102
+ auto_configure_project
103
+ fi
104
+ }
105
+
106
+ # Ensure .gitignore has necessary patterns
107
+ setup_gitignore() {
108
+ local patterns=(
109
+ "node_modules/"
110
+ ".env"
111
+ ".env.local"
112
+ "*.log"
113
+ ".DS_Store"
114
+ ".ralph/last_*"
115
+ ".ralph/screenshots/"
116
+ ".ralph/archive/"
117
+ ".backups/"
118
+ ".claude/settings.json"
119
+ )
120
+
121
+ if [[ ! -f ".gitignore" ]]; then
122
+ printf '%s\n' "${patterns[@]}" > .gitignore
123
+ echo " Created .gitignore"
124
+ else
125
+ local added=false
126
+ for pattern in "${patterns[@]}"; do
127
+ if ! grep -qF "$pattern" .gitignore 2>/dev/null; then
128
+ echo "$pattern" >> .gitignore
129
+ added=true
130
+ fi
131
+ done
132
+ if [[ "$added" == "true" ]]; then
133
+ echo " Updated .gitignore"
134
+ fi
135
+ fi
136
+ }
137
+
138
+ # Install Claude Code hooks
139
+ setup_claude_hooks() {
140
+ local pkg_root="$1"
141
+ local settings_file=".claude/settings.json"
142
+ local src_hooks_dir="$pkg_root/ralph/hooks"
143
+ local project_hooks_dir=".ralph/hooks"
144
+
145
+ echo "Installing Claude Code hooks..."
146
+
147
+ # Check for source hooks directory
148
+ if [[ ! -d "$src_hooks_dir" ]]; then
149
+ print_warning "Hooks directory not found at $src_hooks_dir"
150
+ return 0
151
+ fi
152
+
153
+ # Check for jq
154
+ if ! command -v jq &>/dev/null; then
155
+ print_warning "jq not installed - skipping hooks setup"
156
+ echo " Install jq and run: npx thrivekit setup"
157
+ return 0
158
+ fi
159
+
160
+ # Copy hooks into the project (so they survive package moves)
161
+ mkdir -p "$project_hooks_dir"
162
+ cp "$src_hooks_dir"/*.sh "$project_hooks_dir/" 2>/dev/null || true
163
+ chmod +x "$project_hooks_dir"/*.sh 2>/dev/null || true
164
+ echo " Copied hooks to $project_hooks_dir/"
165
+
166
+ # Get absolute path to project hooks
167
+ local hooks_dir
168
+ hooks_dir="$(cd "$project_hooks_dir" && pwd)"
169
+
170
+ # Create .claude directory
171
+ mkdir -p ".claude"
172
+
173
+ # Create settings file if it doesn't exist
174
+ [[ ! -f "$settings_file" ]] && echo '{}' > "$settings_file"
175
+
176
+ # Build hooks config with absolute paths to project hooks
177
+ local hooks_config
178
+ hooks_config=$(cat <<EOF
179
+ {
180
+ "PreToolUse": [
181
+ {
182
+ "matcher": "Edit|Write",
183
+ "hooks": [
184
+ {"type": "command", "command": "$hooks_dir/protect-prd.sh", "timeout": 5}
185
+ ]
186
+ }
187
+ ],
188
+ "PostToolUse": [
189
+ {
190
+ "matcher": "Edit|Write",
191
+ "hooks": [
192
+ {"type": "command", "command": "$hooks_dir/warn-debug.sh", "timeout": 5},
193
+ {"type": "command", "command": "$hooks_dir/warn-secrets.sh", "timeout": 5},
194
+ {"type": "command", "command": "$hooks_dir/warn-urls.sh", "timeout": 5},
195
+ {"type": "command", "command": "$hooks_dir/warn-empty-catch.sh", "timeout": 5}
196
+ ]
197
+ },
198
+ {
199
+ "matcher": "*",
200
+ "hooks": [
201
+ {"type": "command", "command": "$hooks_dir/log-tools.sh", "timeout": 3}
202
+ ]
203
+ }
204
+ ],
205
+ "SessionStart": [
206
+ {
207
+ "hooks": [
208
+ {"type": "command", "command": "$hooks_dir/inject-context.sh", "timeout": 5}
209
+ ]
210
+ }
211
+ ],
212
+ "Stop": [
213
+ {
214
+ "hooks": [
215
+ {"type": "command", "command": "$hooks_dir/save-learnings.sh", "timeout": 10}
216
+ ]
217
+ }
218
+ ]
219
+ }
220
+ EOF
221
+ )
222
+
223
+ # Merge hooks into settings
224
+ local tmp
225
+ tmp=$(mktemp)
226
+ jq --argjson hooks "$hooks_config" '.hooks = $hooks' "$settings_file" > "$tmp" && mv "$tmp" "$settings_file"
227
+ echo " Configured .claude/settings.json"
228
+ }
229
+
230
+ # Copy slash commands
231
+ setup_slash_commands() {
232
+ local pkg_root="$1"
233
+
234
+ if [[ -d "$pkg_root/.claude/commands" ]]; then
235
+ echo "Installing slash commands..."
236
+ mkdir -p .claude/commands
237
+ cp -r "$pkg_root/.claude/commands/"* .claude/commands/ 2>/dev/null || true
238
+ echo " Copied commands to .claude/commands/"
239
+ fi
240
+ }
241
+
242
+ # Generate CLAUDE.md with detected project info
243
+ setup_claude_md() {
244
+ local marker="<!-- thrivekit-detected -->"
245
+
246
+ # Skip if we already added our section
247
+ [[ -f "CLAUDE.md" ]] && grep -q "$marker" "CLAUDE.md" 2>/dev/null && return 0
248
+
249
+ echo "Generating CLAUDE.md..."
250
+
251
+ local runtime="" framework="" language="" styling="" testing="" structure=""
252
+
253
+ # Detect runtime/language
254
+ local fe_dir=""
255
+ [[ -d "frontend" ]] && fe_dir="frontend"
256
+ [[ -d "client" ]] && fe_dir="client"
257
+ [[ -d "web" ]] && fe_dir="web"
258
+ [[ -d "apps/web" ]] && fe_dir="apps/web"
259
+
260
+ [[ -f "package.json" || -f "${fe_dir}/package.json" ]] && runtime="Node.js"
261
+ [[ -f "Cargo.toml" ]] && runtime="Rust"
262
+ [[ -f "go.mod" ]] && runtime="Go"
263
+ [[ -f "pyproject.toml" || -f "requirements.txt" || -f "manage.py" ]] && runtime="${runtime:+$runtime + }Python"
264
+ [[ -f "Gemfile" ]] && runtime="Ruby"
265
+
266
+ # Detect framework
267
+ local pkg="package.json"
268
+ [[ -n "$fe_dir" && -f "${fe_dir}/package.json" ]] && pkg="${fe_dir}/package.json"
269
+
270
+ if [[ -f "$pkg" ]]; then
271
+ grep -q '"next"' "$pkg" 2>/dev/null && framework="Next.js"
272
+ grep -q '"react"' "$pkg" 2>/dev/null && [[ -z "$framework" ]] && framework="React"
273
+ grep -q '"vue"' "$pkg" 2>/dev/null && framework="Vue"
274
+ grep -q '"svelte"' "$pkg" 2>/dev/null && framework="Svelte"
275
+ grep -q '"express"' "$pkg" 2>/dev/null && framework="${framework:+$framework + }Express"
276
+ fi
277
+ [[ -f "manage.py" ]] && framework="${framework:+$framework + }Django"
278
+
279
+ # Detect TypeScript
280
+ [[ -f "tsconfig.json" || -f "${fe_dir}/tsconfig.json" ]] && language="TypeScript"
281
+
282
+ # Detect styling
283
+ [[ -f "tailwind.config.js" || -f "tailwind.config.ts" || -f "${fe_dir}/tailwind.config.js" || -f "${fe_dir}/tailwind.config.ts" ]] && styling="Tailwind CSS"
284
+
285
+ # Detect testing
286
+ [[ -f "vitest.config.ts" || -f "vitest.config.js" ]] && testing="Vitest"
287
+ [[ -f "jest.config.js" || -f "jest.config.ts" ]] && testing="Jest"
288
+ [[ -f "playwright.config.ts" || -f "playwright.config.js" ]] && testing="${testing:+$testing + }Playwright"
289
+
290
+ # Detect Python package manager
291
+ local python_runner=""
292
+ local api_dir=""
293
+ [[ -d "apps/api" ]] && api_dir="apps/api"
294
+ [[ -d "backend" ]] && api_dir="backend"
295
+ [[ -d "api" ]] && api_dir="api"
296
+
297
+ if [[ -f "uv.lock" ]] || [[ -n "$api_dir" && -f "$api_dir/uv.lock" ]]; then
298
+ python_runner="uv run python"
299
+ elif [[ -f "poetry.lock" ]] || [[ -n "$api_dir" && -f "$api_dir/poetry.lock" ]]; then
300
+ python_runner="poetry run python"
301
+ fi
302
+
303
+ # Build detected info section
304
+ local detected_section="
305
+ $marker
306
+ ## Detected Project Info
307
+
308
+ ${runtime:+- Runtime: $runtime}
309
+ ${framework:+- Framework: $framework}
310
+ ${language:+- Language: $language}
311
+ ${styling:+- Styling: $styling}
312
+ ${testing:+- Testing: $testing}
313
+ ${python_runner:+- Python: Use \`$python_runner\` (not bare \`python\`)}
314
+
315
+ *Auto-detected by thrivekit. Edit freely.*"
316
+
317
+ if [[ -f "CLAUDE.md" ]]; then
318
+ echo "$detected_section" >> CLAUDE.md
319
+ echo " Updated CLAUDE.md"
320
+ else
321
+ cat > CLAUDE.md << EOF
322
+ # Project Guide for Claude
323
+
324
+ ## Your Rules
325
+ <!-- Add your project-specific rules, patterns, and conventions here -->
326
+ $detected_section
327
+ EOF
328
+ echo " Created CLAUDE.md"
329
+ fi
330
+ }
331
+
332
+ # Configure MCP (Chrome DevTools)
333
+ setup_mcp() {
334
+ local claude_json="$HOME/.claude.json"
335
+
336
+ # Skip if jq not available
337
+ command -v jq &>/dev/null || return 0
338
+
339
+ # Create claude.json if it doesn't exist
340
+ [[ ! -f "$claude_json" ]] && echo '{}' > "$claude_json"
341
+
342
+ # Skip if already configured
343
+ jq -e '.mcpServers["chrome-devtools"]' "$claude_json" > /dev/null 2>&1 && return 0
344
+
345
+ echo "Configuring MCP servers..."
346
+ local tmp
347
+ tmp=$(mktemp)
348
+ jq '.mcpServers["chrome-devtools"] = {
349
+ "command": "npx",
350
+ "args": ["-y", "@anthropic-ai/mcp-server-chrome-devtools@0.0.5"]
351
+ }' "$claude_json" > "$tmp" && mv "$tmp" "$claude_json"
352
+ echo " Added chrome-devtools MCP server"
353
+ }
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
+ }