vibefast-cli 1.1.5 → 1.3.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 (301) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +63 -169
  3. package/dist/__tests__/recipes.test.js +89 -85
  4. package/dist/__tests__/recipes.test.js.map +1 -1
  5. package/dist/commands/add.d.ts +1 -1
  6. package/dist/commands/add.d.ts.map +1 -1
  7. package/dist/commands/add.js +576 -588
  8. package/dist/commands/add.js.map +1 -1
  9. package/dist/commands/checklist.d.ts +1 -1
  10. package/dist/commands/checklist.d.ts.map +1 -1
  11. package/dist/commands/checklist.js +40 -39
  12. package/dist/commands/checklist.js.map +1 -1
  13. package/dist/commands/doctor.d.ts +1 -1
  14. package/dist/commands/doctor.js +22 -22
  15. package/dist/commands/doctor.js.map +1 -1
  16. package/dist/commands/env.d.ts +1 -1
  17. package/dist/commands/env.d.ts.map +1 -1
  18. package/dist/commands/env.js +58 -53
  19. package/dist/commands/env.js.map +1 -1
  20. package/dist/commands/health.d.ts +1 -1
  21. package/dist/commands/health.d.ts.map +1 -1
  22. package/dist/commands/health.js +101 -93
  23. package/dist/commands/health.js.map +1 -1
  24. package/dist/commands/init.d.ts +1 -1
  25. package/dist/commands/init.d.ts.map +1 -1
  26. package/dist/commands/init.js +416 -296
  27. package/dist/commands/init.js.map +1 -1
  28. package/dist/commands/remove.d.ts +1 -1
  29. package/dist/commands/remove.d.ts.map +1 -1
  30. package/dist/commands/remove.js +77 -64
  31. package/dist/commands/remove.js.map +1 -1
  32. package/dist/commands/status.d.ts +1 -1
  33. package/dist/commands/status.d.ts.map +1 -1
  34. package/dist/commands/status.js +15 -14
  35. package/dist/commands/status.js.map +1 -1
  36. package/dist/core/__tests__/detect.test.js +68 -34
  37. package/dist/core/__tests__/detect.test.js.map +1 -1
  38. package/dist/core/ast.d.ts +14 -0
  39. package/dist/core/ast.d.ts.map +1 -0
  40. package/dist/core/ast.js +239 -0
  41. package/dist/core/ast.js.map +1 -0
  42. package/dist/core/codemod.d.ts.map +1 -1
  43. package/dist/core/codemod.js +62 -44
  44. package/dist/core/codemod.js.map +1 -1
  45. package/dist/core/config.d.ts +10 -0
  46. package/dist/core/config.d.ts.map +1 -0
  47. package/dist/core/config.js +51 -0
  48. package/dist/core/config.js.map +1 -0
  49. package/dist/core/detect.d.ts +8 -2
  50. package/dist/core/detect.d.ts.map +1 -1
  51. package/dist/core/detect.js +52 -21
  52. package/dist/core/detect.js.map +1 -1
  53. package/dist/core/errors.d.ts.map +1 -1
  54. package/dist/core/errors.js +9 -8
  55. package/dist/core/errors.js.map +1 -1
  56. package/dist/core/exec.d.ts +16 -0
  57. package/dist/core/exec.d.ts.map +1 -0
  58. package/dist/core/exec.js +48 -0
  59. package/dist/core/exec.js.map +1 -0
  60. package/dist/core/manualSteps.d.ts +7 -0
  61. package/dist/core/manualSteps.d.ts.map +1 -0
  62. package/dist/core/manualSteps.js +59 -0
  63. package/dist/core/manualSteps.js.map +1 -0
  64. package/dist/core/paths.d.ts +3 -1
  65. package/dist/core/paths.d.ts.map +1 -1
  66. package/dist/core/paths.js +14 -10
  67. package/dist/core/paths.js.map +1 -1
  68. package/dist/core/spinner.d.ts +1 -1
  69. package/dist/core/spinner.d.ts.map +1 -1
  70. package/dist/core/spinner.js +38 -8
  71. package/dist/core/spinner.js.map +1 -1
  72. package/dist/core/vosk.d.ts.map +1 -1
  73. package/dist/core/vosk.js +50 -39
  74. package/dist/core/vosk.js.map +1 -1
  75. package/docs/manual-testing.md +91 -0
  76. package/package.json +6 -3
  77. package/recipes/audio-recorder/apps/native/src/app/audio-recorder/index.tsx +5 -0
  78. package/recipes/audio-recorder/recipe.json +3 -3
  79. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/components/audio-player.tsx +301 -0
  80. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/components/audio-recorder.tsx +373 -0
  81. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/components/audio-waveform.tsx +270 -0
  82. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/components/index.ts +4 -0
  83. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/components/recording-list.tsx +89 -0
  84. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/audio-player-demo.tsx +66 -0
  85. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/audio-recorder-cloud.tsx +68 -0
  86. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/audio-recorder-interview.tsx +102 -0
  87. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/basic.tsx +27 -0
  88. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/index.ts +5 -0
  89. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/with-recording-list-demo.tsx +82 -0
  90. package/recipes/audio-recorder-supabase/packages/backend/src/services/recordings.ts +369 -0
  91. package/recipes/audio-recorder-supabase/packages/backend/supabase/migrations/recordings.sql +70 -0
  92. package/recipes/audio-recorder-supabase/recipe.json +52 -0
  93. package/recipes/audio-recorder-supabase@latest.zip +0 -0
  94. package/recipes/audio-recorder@latest.zip +0 -0
  95. package/recipes/charts/apps/native/src/features/charts/components/bar-chart.tsx +3 -3
  96. package/recipes/charts/apps/native/src/features/charts/components/candlestick-chart.tsx +2 -2
  97. package/recipes/charts/apps/native/src/features/charts/components/chart-card.tsx +5 -5
  98. package/recipes/charts/apps/native/src/features/charts/components/column-chart.tsx +3 -3
  99. package/recipes/charts/apps/native/src/features/charts/components/doughnut-chart.tsx +20 -4
  100. package/recipes/charts/apps/native/src/features/charts/components/line-chart.tsx +7 -6
  101. package/recipes/charts/apps/native/src/features/charts/components/radar-chart.tsx +6 -4
  102. package/recipes/charts/apps/native/src/features/charts/components/radial-bar-chart.tsx +1 -1
  103. package/recipes/charts/apps/native/src/features/charts/components/stacked-bar-chart.tsx +5 -4
  104. package/recipes/charts/recipe.json +4 -13
  105. package/recipes/charts@latest.zip +0 -0
  106. package/recipes/chatbot/apps/native/src/app/chatbot/index.tsx +1 -0
  107. package/recipes/chatbot/apps/native/src/features/chatbot/components/chat-markdown.tsx +86 -86
  108. package/recipes/chatbot/apps/native/src/features/chatbot/components/markdown/code-block.tsx +86 -53
  109. package/recipes/chatbot/recipe.json +26 -92
  110. package/recipes/chatbot-supabase/apps/native/src/api-client/supabase/chatbot.ts +515 -0
  111. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/app/index.tsx +257 -0
  112. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/chat-header-buttons.tsx +59 -0
  113. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/chat-input-bar.tsx +485 -0
  114. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/chat-markdown.tsx +575 -0
  115. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/chat-message-bubble.tsx +223 -0
  116. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/chat-settings-modal.tsx +161 -0
  117. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/image-preview-list.tsx +116 -0
  118. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/markdown/code-block.tsx +165 -0
  119. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/markdown/index.ts +10 -0
  120. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/markdown/table-renderer.tsx +129 -0
  121. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/message-error-boundary.tsx +78 -0
  122. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/message-list.tsx +170 -0
  123. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/model-selector.tsx +283 -0
  124. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/report-content-modal.tsx +188 -0
  125. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/suggested-messages.tsx +67 -0
  126. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/constants/models.ts +20 -0
  127. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/constants/report-reasons.ts +9 -0
  128. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-attachment-cache.ts +142 -0
  129. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-chat-config.ts +458 -0
  130. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-chat-handlers.ts +429 -0
  131. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-chatbot-settings.ts +89 -0
  132. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-conversation.ts +90 -0
  133. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-image-picker.ts +122 -0
  134. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-keyboard-coordinator.ts +161 -0
  135. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/hooks/use-smart-scroll-manager.ts +213 -0
  136. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/models/index.ts +86 -0
  137. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/models/models.ts +162 -0
  138. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/models/providers.ts +62 -0
  139. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/models/types.ts +40 -0
  140. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/services/file-uploader.ts +287 -0
  141. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/services/message-handler-service.ts +189 -0
  142. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/types/index.ts +70 -0
  143. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/utils/chat-telemetry.ts +91 -0
  144. package/recipes/chatbot-supabase/packages/backend/src/services/conversations.ts +243 -0
  145. package/recipes/chatbot-supabase/packages/backend/src/services/messages.ts +327 -0
  146. package/recipes/chatbot-supabase/packages/backend/supabase/functions/chat-stream/index.ts +347 -0
  147. package/recipes/chatbot-supabase/packages/backend/supabase/migrations/chatbot.sql +104 -0
  148. package/recipes/chatbot-supabase/recipe.json +106 -0
  149. package/recipes/chatbot-supabase@latest.zip +0 -0
  150. package/recipes/chatbot.zip +0 -0
  151. package/recipes/chatbot@latest.zip +0 -0
  152. package/recipes/image-analysis/packages/backend/convex/imageAnalysis/index.ts +2 -2
  153. package/recipes/image-analysis/packages/backend/convex/imageAnalysis.ts +0 -1
  154. package/recipes/image-analysis/recipe.json +15 -55
  155. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/analysis-options-screen.tsx +304 -0
  156. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/camera.tsx +221 -0
  157. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/image-capture-screen.tsx +333 -0
  158. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/loading-screen.tsx +214 -0
  159. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/loading.tsx +191 -0
  160. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/results.tsx +137 -0
  161. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/trait-details.tsx +172 -0
  162. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/use-analysis-data.ts +160 -0
  163. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/app/use-results-screen.ts +151 -0
  164. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/achievement-badge.tsx +77 -0
  165. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/achievement-card.tsx +75 -0
  166. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/achievement-unlocked-modal.tsx +162 -0
  167. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/achievements-section.tsx +44 -0
  168. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/advice-list.tsx +42 -0
  169. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/circular-progress.tsx +233 -0
  170. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/content-card.tsx +38 -0
  171. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/error-state.tsx +42 -0
  172. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/index.ts +9 -0
  173. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/loading-state.tsx +26 -0
  174. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/profile-image.tsx +60 -0
  175. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/results-header.tsx +62 -0
  176. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/score-display.tsx +54 -0
  177. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/share-options-modal.tsx +110 -0
  178. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/components/results/traits-grid.tsx +74 -0
  179. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/config/analysis-config.ts +80 -0
  180. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/config/master-analysis-config.ts +157 -0
  181. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/hooks/index.ts +1 -0
  182. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/hooks/use-analysis.ts +38 -0
  183. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/hooks/use-image-analysis.ts +208 -0
  184. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/services/analysis-service.ts +262 -0
  185. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/services/share-service.ts +176 -0
  186. package/recipes/image-analysis-supabase/apps/native/src/features/image-analyzer/services/trait-details-service.ts +289 -0
  187. package/recipes/image-analysis-supabase/packages/backend/src/services/image-analyses.ts +132 -0
  188. package/recipes/image-analysis-supabase/packages/backend/supabase/functions/analyze-image/index.ts +312 -0
  189. package/recipes/image-analysis-supabase/packages/backend/supabase/migrations/image_analysis.sql +42 -0
  190. package/recipes/image-analysis-supabase/recipe.json +90 -0
  191. package/recipes/image-analysis-supabase@latest.zip +0 -0
  192. package/recipes/image-analysis@latest.zip +0 -0
  193. package/recipes/image-generator/apps/native/src/features/image-generator/app/index.tsx +16 -2
  194. package/recipes/image-generator/apps/native/src/features/image-generator/components/image-model-selector.tsx +11 -5
  195. package/recipes/image-generator/apps/native/src/features/image-generator/hooks/use-image-generator.ts +11 -5
  196. package/recipes/image-generator/packages/backend/convex/imageGeneration/index.ts +2 -2
  197. package/recipes/image-generator/recipe.json +16 -39
  198. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/app/_layout.tsx +26 -0
  199. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/app/gallery.tsx +217 -0
  200. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/app/index.tsx +251 -0
  201. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/components/gallery-image.tsx +25 -0
  202. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/components/image-detail-modal.tsx +215 -0
  203. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/components/image-model-selector.tsx +216 -0
  204. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/components/image-placeholder.tsx +26 -0
  205. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/hooks/use-image-gallery.ts +71 -0
  206. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/hooks/use-image-generator-settings.ts +152 -0
  207. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/hooks/use-image-generator.ts +103 -0
  208. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/models/models.ts +66 -0
  209. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/services/image-gallery-service.ts +96 -0
  210. package/recipes/image-generator-supabase/apps/native/src/features/image-generator/services/image-save-service.ts +120 -0
  211. package/recipes/image-generator-supabase/packages/backend/supabase/functions/generate-image/index.ts +291 -0
  212. package/recipes/image-generator-supabase/packages/backend/supabase/migrations/image_generator.sql +71 -0
  213. package/recipes/image-generator-supabase/recipe.json +86 -0
  214. package/recipes/image-generator-supabase@latest.zip +0 -0
  215. package/recipes/image-generator@latest.zip +0 -0
  216. package/recipes/ios-widget/recipe.json +15 -24
  217. package/recipes/ios-widget@latest.zip +0 -0
  218. package/recipes/onboarding/apps/native/src/features/onboarding/analytics/index.ts +9 -0
  219. package/recipes/onboarding/apps/native/src/features/onboarding/components/onboarding-with-analytics.tsx +141 -0
  220. package/recipes/onboarding/apps/native/src/features/onboarding/components/onboarding.tsx +173 -0
  221. package/recipes/onboarding/apps/native/src/features/onboarding/config/onboarding-flow-config.ts +189 -0
  222. package/recipes/onboarding/apps/native/src/features/onboarding/demo-one/app/index.tsx +42 -0
  223. package/recipes/onboarding/apps/native/src/features/onboarding/demo-one/data.ts +32 -0
  224. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/app/index.tsx +43 -0
  225. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/interactive-onboarding.tsx +222 -0
  226. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/ai-tone-step.tsx +133 -0
  227. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/currency-step.tsx +165 -0
  228. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/feature-ai-step.tsx +199 -0
  229. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/feature-chatbot-step.tsx +154 -0
  230. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/feature-manual-step.tsx +156 -0
  231. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/feature-scan-step.tsx +158 -0
  232. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/main-reason-step.tsx +139 -0
  233. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/notification-step.tsx +129 -0
  234. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/overspend-step.tsx +138 -0
  235. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/personalizing-step.tsx +190 -0
  236. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/rating-step.tsx +98 -0
  237. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/reminder-step.tsx +181 -0
  238. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/safety-step.tsx +110 -0
  239. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/struggle-step.tsx +139 -0
  240. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/welcome-step.tsx +217 -0
  241. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/ui/onboarding-header.tsx +58 -0
  242. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/constants.ts +179 -0
  243. package/recipes/onboarding/apps/native/src/features/onboarding/hooks/use-onboarding-analytics.ts +323 -0
  244. package/recipes/onboarding/apps/native/src/features/onboarding/services/onboarding-analytics.ts +432 -0
  245. package/recipes/onboarding/recipe.json +15 -0
  246. package/recipes/onboarding@latest.zip +0 -0
  247. package/recipes/payments/recipe.json +28 -61
  248. package/recipes/payments-supabase/apps/native/src/features/payments/README.md +200 -0
  249. package/recipes/payments-supabase/apps/native/src/features/payments/app/local-paywall.tsx +194 -0
  250. package/recipes/payments-supabase/apps/native/src/features/payments/app/remote-paywall.tsx +79 -0
  251. package/recipes/payments-supabase/apps/native/src/features/payments/components/payment-initializer.tsx +95 -0
  252. package/recipes/payments-supabase/apps/native/src/features/payments/components/paywall-error-state.tsx +60 -0
  253. package/recipes/payments-supabase/apps/native/src/features/payments/components/paywall-local-mode.tsx +116 -0
  254. package/recipes/payments-supabase/apps/native/src/features/payments/components/paywall-product-card.tsx +133 -0
  255. package/recipes/payments-supabase/apps/native/src/features/payments/components/paywall-remote-mode.tsx +146 -0
  256. package/recipes/payments-supabase/apps/native/src/features/payments/hooks/use-entitlement.ts +63 -0
  257. package/recipes/payments-supabase/apps/native/src/features/payments/index.ts +8 -0
  258. package/recipes/payments-supabase/apps/native/src/features/payments/services/revenuecat-adapter.ts +407 -0
  259. package/recipes/payments-supabase/packages/backend/src/services/payments.ts +201 -0
  260. package/recipes/payments-supabase/packages/backend/supabase/migrations/payments.sql +35 -0
  261. package/recipes/payments-supabase/recipe.json +72 -0
  262. package/recipes/payments-supabase@latest.zip +0 -0
  263. package/recipes/payments@latest.zip +0 -0
  264. package/recipes/quiz/apps/native/src/features/quiz/index.tsx +1 -2
  265. package/recipes/quiz/recipe.json +6 -9
  266. package/recipes/quiz@latest.zip +0 -0
  267. package/recipes/tracker-app/apps/native/src/features/tracker-app/app/index.tsx +1 -2
  268. package/recipes/tracker-app/recipe.json +7 -10
  269. package/recipes/tracker-app@latest.zip +0 -0
  270. package/recipes/voice-bot/recipe.json +8 -68
  271. package/recipes/voice-bot.zip +0 -0
  272. package/recipes/voice-bot@latest.zip +0 -0
  273. package/recipes/wake-word/recipe.json +10 -9
  274. package/recipes/wake-word.zip +0 -0
  275. package/recipes/wake-word@latest.zip +0 -0
  276. package/recipes/charts/apps/native/src/app/(root)/(protected)/charts/index.tsx +0 -3
  277. package/recipes/chatbot/packages/backend/convex/lib/rateLimit.ts +0 -100
  278. package/recipes/chatbot/packages/backend/convex/lib/telemetry.ts +0 -29
  279. package/recipes/chatbot/packages/backend/convex/ragKnowledge.ts +0 -0
  280. package/recipes/image-analysis/apps/native/assets/features/image-analyzer/front.jpg +0 -0
  281. package/recipes/image-analysis/apps/native/assets/features/image-analyzer/side.jpg +0 -0
  282. package/recipes/image-analysis/apps/native/assets/features/image-analyzer/threeQuarter.jpg +0 -0
  283. package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/_layout.tsx +0 -5
  284. package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/analysis-options.tsx +0 -50
  285. package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/camera.tsx +0 -2
  286. package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/index.tsx +0 -50
  287. package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/loading.tsx +0 -50
  288. package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/results.tsx +0 -2
  289. package/recipes/image-analysis/apps/native/src/app/(root)/(protected)/analysis/[type]/trait-details.tsx +0 -3
  290. package/recipes/image-analysis/packages/backend/convex/imageAnalysisFunctions.ts +0 -325
  291. package/recipes/image-analysis/packages/backend/convex/lib/ai/imageAnalysisAdapter.ts +0 -200
  292. package/recipes/payments/apps/native/src/app/(root)/(protected)/paywall/index.tsx +0 -74
  293. package/recipes/payments/apps/native/src/app/(root)/(protected)/paywall/local.tsx +0 -25
  294. package/recipes/payments/apps/native/src/app/(root)/(protected)/paywall/remote.tsx +0 -23
  295. package/recipes/quiz/apps/native/src/app/(root)/(protected)/quiz/index.tsx +0 -47
  296. package/recipes/tracker-app/apps/native/src/app/(root)/(protected)/tracker-app/index.tsx +0 -1
  297. package/recipes/voice-bot/apps/native/src/app/(root)/(protected)/voice-bot/index.tsx +0 -27
  298. package/recipes/voice-bot/packages/backend/convex/router.ts +0 -81
  299. /package/recipes/{chatbot/apps/native/src/app/(root)/(protected) → chatbot-supabase/apps/native/src/app}/chatbot/index.tsx +0 -0
  300. /package/recipes/{image-generator/apps/native/src/app/(root)/(protected) → image-generator-supabase/apps/native/src/app}/image-generator/gallery.tsx +0 -0
  301. /package/recipes/{image-generator/apps/native/src/app/(root)/(protected) → image-generator-supabase/apps/native/src/app}/image-generator/index.tsx +0 -0
@@ -1,330 +1,430 @@
1
- import { Command } from 'commander';
2
- import { log } from '../core/log.js';
3
- import { withSpinner } from '../core/spinner.js';
4
- import { promptSelectAsync, promptUser, promptYesNo } from '../core/prompt.js';
5
- import simpleGit from 'simple-git';
6
- import { existsSync } from 'fs';
7
- import { join } from 'path';
8
- import { exec } from 'child_process';
9
- import { promisify } from 'util';
10
- const execAsync = promisify(exec);
11
- export const initCommand = new Command('init')
12
- .description('Initialize a new VibeFast project')
13
- .option('--license <key>', 'Your VibeFast license key')
14
- .option('--branch <branch>', 'Branch to clone (clean or main, default: clean)')
15
- .option('--yes', 'Skip all prompts and use defaults')
16
- .option('--skip-install', 'Skip dependency installation')
1
+ import { Command, Option } from "commander";
2
+ import { log } from "../core/log.js";
3
+ import { withSpinner } from "../core/spinner.js";
4
+ import { promptSelectAsync, promptUser, promptYesNo, } from "../core/prompt.js";
5
+ import simpleGit from "simple-git";
6
+ import { existsSync } from "fs";
7
+ import { readFile } from "fs/promises";
8
+ import { join } from "path";
9
+ import { runCommand, spawnCommand } from "../core/exec.js";
10
+ import { appendManualSteps } from "../core/manualSteps.js";
11
+ /**
12
+ * Sync Convex environment variables from backend to native/web .env.local files.
13
+ * Reads CONVEX_URL from packages/backend/.env.local, derives CONVEX_SITE_URL,
14
+ * and REPLACES placeholder values in apps/native/.env.local and apps/web/.env.local.
15
+ */
16
+ async function syncConvexEnvToFrontends(projectPath, platforms) {
17
+ const backendEnvPath = join(projectPath, "packages/backend/.env.local");
18
+ // Check if backend env exists
19
+ if (!existsSync(backendEnvPath)) {
20
+ return; // No backend env to sync
21
+ }
22
+ try {
23
+ const backendEnv = await readFile(backendEnvPath, "utf-8");
24
+ // Extract CONVEX_URL from backend env
25
+ const convexUrlMatch = backendEnv.match(/CONVEX_URL=(.+)/);
26
+ if (!convexUrlMatch) {
27
+ return; // No CONVEX_URL found
28
+ }
29
+ const convexUrl = convexUrlMatch[1].trim();
30
+ // Derive CONVEX_SITE_URL by replacing .cloud with .site
31
+ const convexSiteUrl = convexUrl.replace(".convex.cloud", ".convex.site");
32
+ // Helper function to replace placeholder values in env file
33
+ const replaceEnvValues = async (envPath) => {
34
+ if (!existsSync(envPath))
35
+ return false;
36
+ let envContent = await readFile(envPath, "utf-8");
37
+ let changed = false;
38
+ // Replace CONVEX_URL=replace_me or any placeholder with actual value
39
+ if (envContent.match(/CONVEX_URL=.*/)) {
40
+ const newContent = envContent.replace(/CONVEX_URL=.*/g, `CONVEX_URL=${convexUrl}`);
41
+ if (newContent !== envContent) {
42
+ envContent = newContent;
43
+ changed = true;
44
+ }
45
+ }
46
+ // Replace CONVEX_SITE_URL=replace_me or any placeholder with actual value
47
+ if (envContent.match(/CONVEX_SITE_URL=.*/)) {
48
+ const newContent = envContent.replace(/CONVEX_SITE_URL=.*/g, `CONVEX_SITE_URL=${convexSiteUrl}`);
49
+ if (newContent !== envContent) {
50
+ envContent = newContent;
51
+ changed = true;
52
+ }
53
+ }
54
+ if (changed) {
55
+ const { writeFile } = await import("fs/promises");
56
+ await writeFile(envPath, envContent, "utf-8");
57
+ }
58
+ return changed;
59
+ };
60
+ // Sync to native if platform selected
61
+ if (platforms.includes("native")) {
62
+ const nativeEnvPath = join(projectPath, "apps/native/.env.local");
63
+ const synced = await replaceEnvValues(nativeEnvPath);
64
+ if (synced) {
65
+ log.info("✓ Synced Convex URLs to apps/native/.env.local");
66
+ }
67
+ }
68
+ // Sync to web if platform selected
69
+ if (platforms.includes("web")) {
70
+ const webEnvPath = join(projectPath, "apps/web/.env.local");
71
+ const synced = await replaceEnvValues(webEnvPath);
72
+ if (synced) {
73
+ log.info("✓ Synced Convex URLs to apps/web/.env.local");
74
+ }
75
+ }
76
+ }
77
+ catch {
78
+ // Silently fail - env sync is best-effort
79
+ }
80
+ }
81
+ export const initCommand = new Command("init")
82
+ .description("Initialize a new VibeFast project")
83
+ .option("--license <key>", "Your VibeFast license key")
84
+ .option("--branch <branch>", "Branch to clone (clean or main, default: clean)")
85
+ .addOption(new Option("--backend <backend>", "Backend type")
86
+ .choices(["convex", "supabase"])
87
+ .default("convex"))
88
+ .option("--yes", "Skip all prompts and use defaults")
89
+ .option("--skip-install", "Skip dependency installation")
17
90
  .action(async (options) => {
18
91
  try {
19
- log.plain('');
20
- log.info('🚀 VibeFast Project Initialization');
21
- log.plain('');
92
+ log.plain("");
93
+ log.info("🚀 VibeFast Project Initialization");
94
+ log.plain("");
22
95
  // Check if already in a VibeFast project
23
- if (existsSync('.vibefast-signature.json')) {
24
- log.error('Already in a VibeFast project!');
25
- log.info('This directory already contains a VibeFast project.');
96
+ if (existsSync(".vibefast-signature.json")) {
97
+ log.error("Already in a VibeFast project!");
98
+ log.info("This directory already contains a VibeFast project.");
26
99
  process.exit(1);
27
100
  }
28
101
  // Step 0: Check License Key (REQUIRED)
29
102
  let licenseKey = options.license;
30
103
  // Check if user already has a license key saved
31
104
  if (!licenseKey) {
32
- const { getToken } = await import('../core/auth.js');
105
+ const { getToken } = await import("../core/auth.js");
33
106
  const existingToken = await getToken();
34
107
  if (existingToken) {
35
- log.info('✓ Using existing license key');
108
+ log.info("✓ Using existing license key");
36
109
  licenseKey = existingToken;
37
110
  }
38
111
  }
39
112
  if (!licenseKey) {
40
- log.plain('');
41
- log.info('🔑 VibeFast License Key Required');
42
- log.plain('');
43
- log.info('To use VibeFast, you need a valid license key.');
44
- log.info('Get one at: https://vibefast.pro');
45
- log.plain('');
113
+ log.plain("");
114
+ log.info("🔑 VibeFast License Key Required");
115
+ log.plain("");
116
+ log.info("To use VibeFast, you need a valid license key.");
117
+ log.info("Get one at: https://vibefast.pro");
118
+ log.plain("");
46
119
  if (options.yes) {
47
120
  log.error('License key is required. Use --license <key> or run "vf login" first');
48
121
  process.exit(1);
49
122
  }
50
- licenseKey = await promptUser('Enter your license key: ');
51
- if (!licenseKey || licenseKey.trim() === '') {
52
- log.error('License key cannot be empty');
123
+ licenseKey = await promptUser("Enter your license key: ");
124
+ if (!licenseKey || licenseKey.trim() === "") {
125
+ log.error("License key cannot be empty");
53
126
  process.exit(1);
54
127
  }
55
128
  // Save globally for future use
56
- const { saveToken } = await import('../core/auth.js');
129
+ const { saveToken } = await import("../core/auth.js");
57
130
  await saveToken(licenseKey);
58
131
  }
59
- // Validate branch option and default to clean
60
- const branch = options.branch === 'main' ? 'main' : 'clean';
61
- if (options.branch && options.branch !== 'clean' && options.branch !== 'main') {
132
+ // Note: Branch will be derived from backend choice after selection
133
+ // For now just validate if branch is passed explicitly
134
+ if (options.branch &&
135
+ options.branch !== "clean" &&
136
+ options.branch !== "main") {
62
137
  log.error('Invalid branch. Use "clean" or "main".');
63
138
  process.exit(1);
64
139
  }
65
140
  // Step 1.5: Choose platforms
66
- let platforms = ['native', 'web'];
141
+ let platforms = ["native", "web"];
67
142
  if (!options.yes) {
68
- const platformChoice = await promptSelectAsync('Which platforms do you want to include?', [
143
+ const platformChoice = (await promptSelectAsync("Which platforms do you want to include?", [
69
144
  {
70
- value: 'both',
71
- label: 'Both (Native + Web)',
72
- description: 'Full monorepo with mobile and web app',
145
+ value: "both",
146
+ label: "Both (Native + Web)",
147
+ description: "Full monorepo with mobile and web app",
73
148
  },
74
149
  {
75
- value: 'native',
76
- label: 'Native Only (Expo)',
77
- description: 'Mobile app only (iOS & Android)',
150
+ value: "native",
151
+ label: "Native Only (Expo)",
152
+ description: "Mobile app only (iOS & Android)",
78
153
  },
79
154
  {
80
- value: 'web',
81
- label: 'Web Only (Next.js)',
82
- description: 'Web app only',
155
+ value: "web",
156
+ label: "Web Only (Next.js)",
157
+ description: "Web app only",
83
158
  },
84
- ]);
85
- if (platformChoice === 'native') {
86
- platforms = ['native'];
87
- log.info('Selected: Native (Expo) only');
159
+ ]));
160
+ if (platformChoice === "native") {
161
+ platforms = ["native"];
162
+ log.info("Selected: Native (Expo) only");
88
163
  }
89
- else if (platformChoice === 'web') {
90
- platforms = ['web'];
91
- log.info('Selected: Web (Next.js) only');
164
+ else if (platformChoice === "web") {
165
+ platforms = ["web"];
166
+ log.info("Selected: Web (Next.js) only");
92
167
  }
93
168
  else {
94
- platforms = ['native', 'web'];
95
- log.info('Selected: Both platforms');
169
+ platforms = ["native", "web"];
170
+ log.info("Selected: Both platforms");
96
171
  }
97
172
  }
98
173
  else {
99
- log.info('Using default: Both platforms');
174
+ log.info("Using default: Both platforms");
175
+ }
176
+ log.plain("");
177
+ // Step 1.6: Choose backend
178
+ let backend = options.backend || "convex";
179
+ if (!options.yes) {
180
+ backend = (await promptSelectAsync("Which backend do you want to use?", [
181
+ {
182
+ value: "convex",
183
+ label: "Convex",
184
+ description: "Real-time sync, serverless functions",
185
+ },
186
+ {
187
+ value: "supabase",
188
+ label: "Supabase",
189
+ description: "PostgreSQL, auth, storage",
190
+ },
191
+ ]));
192
+ log.info(`Selected: ${backend === "convex" ? "Convex" : "Supabase"} backend`);
193
+ }
194
+ else {
195
+ log.info(`Using backend: ${backend}`);
100
196
  }
101
- log.plain('');
197
+ log.plain("");
198
+ // Derive the branch based on backend (both clean and custom clone the appropriate clean branch)
199
+ const branch = backend === "supabase" ? "supabase-clean" : "clean";
102
200
  // Step 1: Choose starter type (after platform selection)
103
- let starterType = 'clean';
201
+ let starterType = "clean";
104
202
  let selectedFeatures = [];
105
203
  if (!options.yes) {
106
- starterType = await promptSelectAsync('How would you like to start?', [
204
+ starterType = (await promptSelectAsync("How would you like to start?", [
107
205
  {
108
- value: 'clean',
109
- label: 'Clean',
110
- description: 'Minimal setup - add features later with vf add',
206
+ value: "clean",
207
+ label: "Clean",
208
+ description: "Minimal setup - add features later with vf add",
111
209
  },
112
210
  {
113
- value: 'custom',
114
- label: 'Custom',
115
- description: 'Choose features now - one-shot setup',
211
+ value: "custom",
212
+ label: "Custom",
213
+ description: "Choose features now - one-shot setup",
116
214
  },
117
- ]);
118
- log.info(`Selected: ${starterType === 'clean' ? 'Clean starter' : 'Custom setup'}`);
119
- log.plain('');
215
+ ]));
216
+ log.info(`Selected: ${starterType === "clean" ? "Clean starter" : "Custom setup"}`);
217
+ log.plain("");
120
218
  // If custom, let them choose features (only when native is present; current feature set is native-only)
121
- if (starterType === 'custom') {
122
- if (!platforms.includes('native')) {
123
- log.warn('⚠ No installable features for Web-only at the moment. Skipping feature selection.');
219
+ if (starterType === "custom") {
220
+ if (!platforms.includes("native")) {
221
+ log.warn("⚠ No installable features for Web-only at the moment. Skipping feature selection.");
124
222
  }
125
223
  else {
126
- const { promptMultiSelectAsync } = await import('../core/prompt.js');
127
- const { getRecipesByCategory } = await import('../core/recipes.js');
128
- log.info('📦 Select features to include:');
129
- log.plain('');
130
- const nativeFeatures = getRecipesByCategory('feature').map(r => ({
224
+ const { promptMultiSelectAsync } = await import("../core/prompt.js");
225
+ const { getRecipesByCategory } = await import("../core/recipes.js");
226
+ log.info("📦 Select features to include:");
227
+ log.plain("");
228
+ const nativeFeatures = getRecipesByCategory("feature").map((r) => ({
131
229
  value: r.name,
132
230
  label: `${r.icon} ${r.name}`,
133
231
  description: r.description,
134
232
  }));
135
- selectedFeatures = await promptMultiSelectAsync('Select features (use space to toggle, enter to confirm):', nativeFeatures);
233
+ selectedFeatures = await promptMultiSelectAsync("Select features (use space to toggle, enter to confirm):", nativeFeatures);
136
234
  if (selectedFeatures.length > 0) {
137
- log.info(`Selected ${selectedFeatures.length} feature(s): ${selectedFeatures.join(', ')}`);
235
+ log.info(`Selected ${selectedFeatures.length} feature(s): ${selectedFeatures.join(", ")}`);
138
236
  }
139
237
  else {
140
- log.info('No features selected - starting with clean setup');
238
+ log.info("No features selected - starting with clean setup");
141
239
  }
142
- log.plain('');
240
+ log.plain("");
143
241
  }
144
242
  }
145
243
  }
146
244
  else {
147
- log.info('Using default: Clean starter');
148
- log.plain('');
245
+ log.info("Using default: Clean starter");
246
+ log.plain("");
149
247
  }
150
248
  // Step 2: Get project name
151
- const defaultName = 'vibefast-app';
249
+ const defaultName = "vibefast-app";
152
250
  let projectName = defaultName;
153
251
  if (!options.yes) {
154
- projectName = await promptUser('Project name: ') || defaultName;
252
+ projectName = (await promptUser("Project name: ")) || defaultName;
155
253
  }
156
254
  projectName = (projectName || defaultName).trim();
157
255
  if (!projectName) {
158
256
  projectName = defaultName;
159
257
  }
160
258
  if (!/^[a-zA-Z0-9_-]+$/.test(projectName)) {
161
- log.error('Project name must use only letters, numbers, hyphens, and underscores.');
259
+ log.error("Project name must use only letters, numbers, hyphens, and underscores.");
162
260
  process.exit(1);
163
261
  }
164
262
  // Validate project name
165
263
  if (existsSync(projectName)) {
166
264
  log.error(`Directory "${projectName}" already exists!`);
167
- log.info('Please choose a different name or remove the existing directory.');
265
+ log.info("Please choose a different name or remove the existing directory.");
168
266
  process.exit(1);
169
267
  }
170
- log.plain('');
268
+ log.plain("");
171
269
  // Step 3: Clone repository
172
270
  // Note: This is a private repo, so we need GitHub credentials
173
271
  // Users can authenticate via:
174
272
  // 1. SSH key (recommended)
175
273
  // 2. GitHub CLI (gh auth)
176
274
  // 3. Personal access token in URL
177
- const repoUrl = 'https://github.com/mzafarr/vibefast-pro.git';
275
+ const repoUrl = "https://github.com/mzafarr/vibefast-pro.git";
178
276
  await withSpinner(`Cloning VibeFast monorepo (${branch} branch)...`, async () => {
179
277
  const git = simpleGit();
180
- const cloneOptions = ['--depth', '1'];
278
+ const cloneOptions = ["--depth", "1"];
181
279
  // Use selected branch (default: clean)
182
- cloneOptions.unshift('--branch', branch, '--single-branch');
280
+ cloneOptions.unshift("--branch", branch, "--single-branch");
183
281
  try {
184
282
  await git.clone(repoUrl, projectName, cloneOptions);
185
283
  }
186
284
  catch (error) {
187
285
  // Handle authentication errors
188
- if (error.message.includes('Authentication failed') ||
189
- error.message.includes('permission denied') ||
190
- error.message.includes('fatal: could not read Username')) {
191
- throw new Error('GitHub authentication failed. This is a private repository.\n\n' +
192
- 'To access it, you need to authenticate with GitHub:\n\n' +
193
- '1. SSH Key (Recommended):\n' +
194
- ' - Set up SSH: https://docs.github.com/en/authentication/connecting-to-github-with-ssh\n' +
195
- ' - Then try again\n\n' +
196
- '2. GitHub CLI:\n' +
197
- ' - Install: https://cli.github.com\n' +
198
- ' - Run: gh auth login\n' +
199
- ' - Then try again\n\n' +
200
- '3. Personal Access Token:\n' +
201
- ' - Create token: https://github.com/settings/tokens\n' +
202
- ' - Use: https://YOUR_TOKEN@github.com/mzafarr/vibefast-pro.git');
286
+ if (error.message.includes("Authentication failed") ||
287
+ error.message.includes("permission denied") ||
288
+ error.message.includes("fatal: could not read Username")) {
289
+ throw new Error("GitHub authentication failed. This is a private repository.\n\n" +
290
+ "To access it, you need to authenticate with GitHub:\n\n" +
291
+ "1. SSH Key (Recommended):\n" +
292
+ " - Set up SSH: https://docs.github.com/en/authentication/connecting-to-github-with-ssh\n" +
293
+ " - Then try again\n\n" +
294
+ "2. GitHub CLI:\n" +
295
+ " - Install: https://cli.github.com\n" +
296
+ " - Run: gh auth login\n" +
297
+ " - Then try again\n\n" +
298
+ "3. Personal Access Token:\n" +
299
+ " - Create token: https://github.com/settings/tokens\n" +
300
+ " - Use: https://YOUR_TOKEN@github.com/mzafarr/vibefast-pro.git");
203
301
  }
204
302
  throw error;
205
303
  }
206
304
  }, {
207
305
  successText: `✓ Repository cloned successfully`,
208
306
  });
209
- log.plain('');
307
+ log.plain("");
210
308
  // Step 3.5: Remove unwanted platforms
211
309
  const projectPath = join(process.cwd(), projectName);
212
310
  if (platforms.length < 2) {
213
- await withSpinner('Cleaning up unwanted platforms...', async () => {
214
- const { rmSync } = await import('fs');
215
- const { join } = await import('path');
216
- if (!platforms.includes('native')) {
311
+ await withSpinner("Cleaning up unwanted platforms...", async () => {
312
+ const { rmSync } = await import("fs");
313
+ const { join } = await import("path");
314
+ if (!platforms.includes("native")) {
217
315
  // Remove native app
218
- const nativePath = join(projectPath, 'apps/native');
316
+ const nativePath = join(projectPath, "apps/native");
219
317
  if (existsSync(nativePath)) {
220
318
  rmSync(nativePath, { recursive: true, force: true });
221
319
  }
222
- log.info(' ✓ Removed native app');
320
+ log.info(" ✓ Removed native app");
223
321
  }
224
- if (!platforms.includes('web')) {
322
+ if (!platforms.includes("web")) {
225
323
  // Remove web app
226
- const webPath = join(projectPath, 'apps/web');
324
+ const webPath = join(projectPath, "apps/web");
227
325
  if (existsSync(webPath)) {
228
326
  rmSync(webPath, { recursive: true, force: true });
229
327
  }
230
- log.info(' ✓ Removed web app');
328
+ log.info(" ✓ Removed web app");
231
329
  }
232
330
  // Update package.json workspaces
233
- const rootPackageJsonPath = join(projectPath, 'package.json');
331
+ const rootPackageJsonPath = join(projectPath, "package.json");
234
332
  if (existsSync(rootPackageJsonPath)) {
235
- const { readFileSync, writeFileSync } = await import('fs');
236
- const packageJson = JSON.parse(readFileSync(rootPackageJsonPath, 'utf-8'));
333
+ const { readFileSync, writeFileSync } = await import("fs");
334
+ const packageJson = JSON.parse(readFileSync(rootPackageJsonPath, "utf-8"));
237
335
  if (packageJson.workspaces) {
238
336
  const workspaces = packageJson.workspaces.filter((ws) => {
239
- if (!platforms.includes('native') && ws.includes('apps/native'))
337
+ if (!platforms.includes("native") &&
338
+ ws.includes("apps/native"))
240
339
  return false;
241
- if (!platforms.includes('web') && ws.includes('apps/web'))
340
+ if (!platforms.includes("web") && ws.includes("apps/web"))
242
341
  return false;
243
342
  return true;
244
343
  });
245
344
  packageJson.workspaces = workspaces;
246
345
  writeFileSync(rootPackageJsonPath, JSON.stringify(packageJson, null, 2));
247
- log.info(' ✓ Updated workspace configuration');
346
+ log.info(" ✓ Updated workspace configuration");
248
347
  }
249
348
  }
250
349
  // Update turbo.json
251
- const turboJsonPath = join(projectPath, 'turbo.json');
350
+ const turboJsonPath = join(projectPath, "turbo.json");
252
351
  if (existsSync(turboJsonPath)) {
253
- const { readFileSync, writeFileSync } = await import('fs');
254
- const turboJson = JSON.parse(readFileSync(turboJsonPath, 'utf-8'));
352
+ const { readFileSync, writeFileSync } = await import("fs");
353
+ const turboJson = JSON.parse(readFileSync(turboJsonPath, "utf-8"));
255
354
  if (turboJson.pipeline) {
256
355
  // Remove platform-specific tasks
257
356
  const tasksToRemove = [];
258
- if (!platforms.includes('native')) {
259
- tasksToRemove.push('native:*', 'dev:native', 'build:native');
357
+ if (!platforms.includes("native")) {
358
+ tasksToRemove.push("native:*", "dev:native", "build:native");
260
359
  }
261
- if (!platforms.includes('web')) {
262
- tasksToRemove.push('web:*', 'dev:web', 'build:web');
360
+ if (!platforms.includes("web")) {
361
+ tasksToRemove.push("web:*", "dev:web", "build:web");
263
362
  }
264
- tasksToRemove.forEach(task => {
363
+ tasksToRemove.forEach((task) => {
265
364
  if (turboJson.pipeline[task]) {
266
365
  delete turboJson.pipeline[task];
267
366
  }
268
367
  });
269
368
  writeFileSync(turboJsonPath, JSON.stringify(turboJson, null, 2));
270
- log.info(' ✓ Updated Turbo configuration');
369
+ log.info(" ✓ Updated Turbo configuration");
271
370
  }
272
371
  }
273
372
  }, {
274
- successText: '✓ Platform cleanup complete',
373
+ successText: "✓ Platform cleanup complete",
275
374
  });
276
- log.plain('');
375
+ log.plain("");
277
376
  }
278
377
  // Step 4: Check for pnpm and install dependencies
279
378
  if (!options.skipInstall) {
280
379
  // Check if pnpm is installed
281
- log.info('Checking for pnpm...');
380
+ log.info("Checking for pnpm...");
282
381
  try {
283
- await execAsync('pnpm --version');
284
- log.info('✓ pnpm is installed');
382
+ await runCommand("pnpm --version");
383
+ log.info("✓ pnpm is installed");
285
384
  }
286
385
  catch {
287
- log.warn('pnpm is not installed');
288
- log.plain('');
289
- const shouldInstallPnpm = options.yes || await promptYesNo('Would you like to install pnpm globally?', true);
386
+ log.warn("pnpm is not installed");
387
+ log.plain("");
388
+ const shouldInstallPnpm = options.yes ||
389
+ (await promptYesNo("Would you like to install pnpm globally?", true));
290
390
  if (shouldInstallPnpm) {
291
- log.plain('');
292
- await withSpinner('Installing pnpm globally...', async () => {
293
- await execAsync('npm install -g pnpm');
391
+ log.plain("");
392
+ await withSpinner("Installing pnpm globally...", async () => {
393
+ await runCommand("npm install -g pnpm");
294
394
  }, {
295
- successText: '✓ pnpm installed',
395
+ successText: "✓ pnpm installed",
296
396
  });
297
397
  }
298
398
  else {
299
- log.warn('Skipping pnpm installation. You can install it later with:');
300
- log.plain(' npm install -g pnpm');
301
- log.plain('');
399
+ log.warn("Skipping pnpm installation. You can install it later with:");
400
+ log.plain(" npm install -g pnpm");
401
+ log.plain("");
302
402
  }
303
403
  }
304
- log.plain('');
305
- await withSpinner('Installing dependencies (this may take a few minutes)...', async () => {
306
- await execAsync('pnpm install', { cwd: projectPath });
404
+ log.plain("");
405
+ await withSpinner("Installing dependencies...", async () => {
406
+ await runCommand("pnpm install", { cwd: projectPath });
307
407
  }, {
308
- successText: '✓ Dependencies installed',
408
+ successText: "✓ Dependencies installed",
309
409
  });
310
- log.plain('');
410
+ log.plain("");
311
411
  }
312
412
  // Step 5: Create .env files from examples (before starter setup)
313
- log.plain('');
314
- log.info('📝 Setting up environment files...');
413
+ log.plain("");
414
+ log.info("📝 Setting up environment files...");
315
415
  const envFiles = [
316
- { example: '.env.local.example', target: '.env.local' },
317
- { example: '.env.staging.example', target: '.env.staging' },
318
- { example: '.env.production.example', target: '.env.production' },
416
+ { example: ".env.local.example", target: ".env.local" },
417
+ { example: ".env.staging.example", target: ".env.staging" },
418
+ { example: ".env.production.example", target: ".env.production" },
319
419
  ];
320
420
  let envFilesCreated = 0;
321
421
  // Setup Native Env Files
322
- if (platforms.includes('native')) {
422
+ if (platforms.includes("native")) {
323
423
  for (const { example, target } of envFiles) {
324
- const examplePath = join(projectPath, 'apps/native', example);
325
- const targetPath = join(projectPath, 'apps/native', target);
424
+ const examplePath = join(projectPath, "apps/native", example);
425
+ const targetPath = join(projectPath, "apps/native", target);
326
426
  if (existsSync(examplePath) && !existsSync(targetPath)) {
327
- const { copyFileSync } = await import('fs');
427
+ const { copyFileSync } = await import("fs");
328
428
  copyFileSync(examplePath, targetPath);
329
429
  log.info(`✓ Created apps/native/${target}`);
330
430
  envFilesCreated++;
@@ -332,12 +432,12 @@ export const initCommand = new Command('init')
332
432
  }
333
433
  }
334
434
  // Setup Web Env Files
335
- if (platforms.includes('web')) {
435
+ if (platforms.includes("web")) {
336
436
  for (const { example, target } of envFiles) {
337
- const examplePath = join(projectPath, 'apps/web', example);
338
- const targetPath = join(projectPath, 'apps/web', target);
437
+ const examplePath = join(projectPath, "apps/web", example);
438
+ const targetPath = join(projectPath, "apps/web", target);
339
439
  if (existsSync(examplePath) && !existsSync(targetPath)) {
340
- const { copyFileSync } = await import('fs');
440
+ const { copyFileSync } = await import("fs");
341
441
  copyFileSync(examplePath, targetPath);
342
442
  log.info(`✓ Created apps/web/${target}`);
343
443
  envFilesCreated++;
@@ -345,95 +445,124 @@ export const initCommand = new Command('init')
345
445
  }
346
446
  }
347
447
  if (envFilesCreated > 0) {
348
- log.plain('');
349
- log.warn('⚠️ Environment files created with placeholder values');
350
- log.info('You will need to fill in your actual API keys and credentials');
351
- log.plain('');
448
+ log.plain("");
449
+ log.warn("⚠️ Environment files created with placeholder values");
450
+ log.info("You will need to fill in your actual API keys and credentials");
451
+ log.plain("");
452
+ // Add base manual steps for env filling
453
+ const baseSteps = [];
454
+ if (platforms.includes("native")) {
455
+ baseSteps.push({
456
+ title: "Fill native env values",
457
+ description: "apps/native/.env.local (and staging/production) were created with placeholders. Add your real values (bundle IDs, URLs, keys).",
458
+ file: "apps/native/.env.local",
459
+ });
460
+ }
461
+ if (platforms.includes("web")) {
462
+ baseSteps.push({
463
+ title: "Fill web env values",
464
+ description: "apps/web/.env.local (and staging/production) were created with placeholders. Add your real values (NEXT auth keys, URLs, etc.).",
465
+ file: "apps/web/.env.local",
466
+ });
467
+ }
468
+ // Backend env file is common; add if present
469
+ const backendEnvPath = join(projectPath, "packages/backend/.env.local");
470
+ if (existsSync(backendEnvPath)) {
471
+ baseSteps.push({
472
+ title: "Fill backend env values",
473
+ description: "packages/backend/.env.local exists with placeholders. Add Convex/Sentry/PostHog/other backend keys.",
474
+ file: "packages/backend/.env.local",
475
+ });
476
+ }
477
+ if (baseSteps.length > 0) {
478
+ const rel = appendManualSteps(projectPath, "init", baseSteps);
479
+ if (rel) {
480
+ log.info(`Manual steps saved to ${rel}`);
481
+ log.plain("");
482
+ }
483
+ }
352
484
  }
353
485
  else {
354
- log.info('ℹ️ No .env example files found to copy');
355
- log.plain('');
486
+ log.info("ℹ️ No .env example files found to copy");
487
+ log.plain("");
356
488
  }
357
489
  // Step 6: Run starter setup script (optional)
358
- const nativeSetupPath = join(projectPath, 'apps/native/scripts/starter-setup.mjs');
359
- const rootSetupPath = join(projectPath, 'scripts/starter-setup.mjs');
360
- const setupScriptPath = existsSync(rootSetupPath) ? rootSetupPath : nativeSetupPath;
361
- if (platforms.includes('native') && existsSync(setupScriptPath)) {
362
- log.info('📋 Starter Setup Available');
363
- log.plain('');
364
- log.info('The starter includes a setup script that will:');
365
- log.plain(' • Fix Expo SDK dependencies');
366
- log.plain(' • Set up Convex backend (if available)');
367
- log.plain('Configure Convex Auth');
368
- log.plain('');
369
- const runSetup = options.yes || await promptYesNo('Would you like to run the starter setup now?', true);
490
+ const nativeSetupPath = join(projectPath, "apps/native/scripts/starter-setup.mjs");
491
+ const rootSetupPath = join(projectPath, "scripts/starter-setup.mjs");
492
+ const setupScriptPath = existsSync(rootSetupPath)
493
+ ? rootSetupPath
494
+ : nativeSetupPath;
495
+ if (platforms.includes("native") && existsSync(setupScriptPath)) {
496
+ log.info("📋 Starter Setup Available");
497
+ log.plain("");
498
+ log.info("The starter includes a setup script that will:");
499
+ log.plain("Fix Expo SDK dependencies");
500
+ log.plain(" • Set up Convex backend (if available)");
501
+ log.plain(" • Configure Convex Auth");
502
+ log.plain("");
503
+ const runSetup = options.yes ||
504
+ (await promptYesNo("Would you like to run the starter setup now?", true));
370
505
  if (runSetup) {
371
- log.plain('');
372
- log.info('Running starter setup...');
373
- log.plain('');
506
+ log.plain("");
507
+ log.info("Running starter setup...");
508
+ log.plain("");
374
509
  try {
375
- // Use spawn instead of execAsync for interactive scripts
376
- const { spawn } = await import('child_process');
377
- await new Promise((resolve, reject) => {
378
- const child = spawn('node', [setupScriptPath], {
379
- cwd: projectPath,
380
- stdio: 'inherit',
381
- });
382
- child.on('close', (code) => {
383
- if (code === 0)
384
- resolve();
385
- else
386
- reject(new Error(`Setup script exited with code ${code}`));
387
- });
388
- child.on('error', reject);
510
+ // Use spawnCommand for interactive scripts
511
+ await spawnCommand("node", [setupScriptPath], {
512
+ cwd: projectPath,
513
+ stdio: "inherit",
389
514
  });
390
- log.plain('');
391
- log.success('✓ Starter setup completed');
515
+ log.plain("");
516
+ log.success("✓ Starter setup completed");
517
+ // Sync Convex env vars to frontend .env.local files
518
+ if (backend === "convex") {
519
+ await syncConvexEnvToFrontends(projectPath, platforms);
520
+ }
392
521
  }
393
522
  catch (error) {
394
- log.warn('⚠ Starter setup had some issues');
395
- log.info('You can run it manually later with:');
523
+ log.warn("⚠ Starter setup had some issues");
524
+ log.info("You can run it manually later with:");
396
525
  log.plain(` cd ${projectName}/apps/native`);
397
- log.plain(' node scripts/starter-setup.mjs');
526
+ log.plain(" node scripts/starter-setup.mjs");
398
527
  }
399
528
  }
400
529
  else {
401
- log.info('You can run the setup script later with:');
530
+ log.info("You can run the setup script later with:");
402
531
  log.plain(` cd ${projectName}/apps/native`);
403
- log.plain(' node scripts/starter-setup.mjs');
532
+ log.plain(" node scripts/starter-setup.mjs");
404
533
  }
405
- log.plain('');
534
+ log.plain("");
406
535
  }
407
- else if (platforms.includes('native')) {
536
+ else if (platforms.includes("native")) {
408
537
  // Native platform selected but script missing
409
- log.warn('⚠ Setup script not found at apps/native/scripts/starter-setup.mjs');
410
- log.info('Skipping automated setup.');
411
- log.plain('');
538
+ log.warn("⚠ Setup script not found at apps/native/scripts/starter-setup.mjs");
539
+ log.info("Skipping automated setup.");
540
+ log.plain("");
412
541
  }
413
542
  // Step 6.5: Install selected features (if custom setup)
414
- if (starterType === 'custom' && selectedFeatures.length > 0) {
415
- log.plain('');
416
- log.info('📦 Installing selected features...');
417
- log.plain('');
543
+ if (starterType === "custom" && selectedFeatures.length > 0) {
544
+ log.plain("");
545
+ log.info("📦 Installing selected features...");
546
+ log.plain("");
418
547
  // We need to be in the project directory for feature installation
419
548
  const originalCwd = process.cwd();
420
549
  process.chdir(projectPath);
421
550
  try {
422
551
  for (const featureName of selectedFeatures) {
423
552
  try {
424
- log.plain('');
425
- log.plain(''.repeat(60));
553
+ log.plain("");
554
+ log.plain("".repeat(60));
426
555
  log.info(`Installing ${featureName}...`);
427
- log.plain('');
428
- if (!platforms.includes('native')) {
556
+ log.plain("");
557
+ if (!platforms.includes("native")) {
429
558
  log.warn(`⚠ Skipping ${featureName} (requires native platform)`);
430
559
  continue;
431
560
  }
432
561
  // Reuse the full add command logic to ensure env, deps, nav, and journal are handled consistently.
433
- const { installFeature } = await import('./add.js');
434
- const { getPaths } = await import('../core/paths.js');
435
- const paths = getPaths(projectPath);
436
- await installFeature(featureName, { yes: true, force: true, target: 'native' }, paths);
562
+ const { installFeature } = await import("./add.js");
563
+ const { getPaths } = await import("../core/paths.js");
564
+ const paths = await getPaths(projectPath);
565
+ await installFeature(featureName, { yes: true, force: true, target: "native" }, paths);
437
566
  log.success(`✓ ${featureName} installed successfully!`);
438
567
  }
439
568
  catch (error) {
@@ -446,98 +575,89 @@ export const initCommand = new Command('init')
446
575
  // Go back to original directory even if installation fails
447
576
  process.chdir(originalCwd);
448
577
  }
449
- log.plain('');
450
- log.plain(''.repeat(60));
578
+ log.plain("");
579
+ log.plain("".repeat(60));
451
580
  log.success(`✓ Installed ${selectedFeatures.length} feature(s)`);
452
- log.plain('');
453
- }
454
- // Step 7: Save license key to project
455
- // Note: License key was already validated in Step 0
456
- log.plain('');
457
- await withSpinner('Saving license key...', async () => {
458
- // Save token to auth file in the new project
459
- const { writeFileContent } = await import('../core/fsx.js');
460
- const authPath = join(projectPath, '.vibefast-auth.json');
461
- await writeFileContent(authPath, JSON.stringify({ token: licenseKey }, null, 2), { force: true });
462
- }, {
463
- successText: '✓ License key saved',
464
- });
465
- log.plain('');
581
+ log.plain("");
582
+ }
583
+ // Step 7: License key was already validated and saved globally in Step 0 (line 86-87)
584
+ // No need to write to project directory - this avoids accidental secret commits
466
585
  // Success!
467
- log.plain('');
468
- log.success('🎉 Project initialized successfully!');
469
- log.plain('');
470
- log.info(`Setup: ${starterType === 'clean' ? 'Clean' : 'Custom'}`);
471
- log.info(`Platforms: ${platforms.join(' + ')}`);
472
- if (starterType === 'custom' && selectedFeatures.length > 0) {
473
- log.info(`Features: ${selectedFeatures.join(', ')}`);
474
- }
475
- log.plain('');
476
- log.info('Next steps:');
586
+ log.plain("");
587
+ log.success("🎉 Project initialized successfully!");
588
+ log.plain("");
589
+ log.info(`Setup: ${starterType === "clean" ? "Clean" : "Custom"}`);
590
+ log.info(`Platforms: ${platforms.join(" + ")}`);
591
+ if (starterType === "custom" && selectedFeatures.length > 0) {
592
+ log.info(`Features: ${selectedFeatures.join(", ")}`);
593
+ }
594
+ log.plain("");
595
+ log.info("Next steps:");
477
596
  log.plain(` 1. cd ${projectName}`);
478
597
  // Check if setup script exists (only for native)
479
- const setupScriptExists = platforms.includes('native') &&
480
- existsSync(join(projectPath, 'apps/native/scripts/starter-setup.mjs'));
598
+ const setupScriptExists = platforms.includes("native") &&
599
+ existsSync(join(projectPath, "apps/native/scripts/starter-setup.mjs"));
481
600
  if (setupScriptExists && !options.skipInstall) {
482
- log.plain(' 2. Start developing:');
483
- if (platforms.includes('native')) {
484
- log.plain(' • pnpm dev:native (start native dev server)');
485
- log.plain(' • pnpm native:ios (run on iOS)');
486
- log.plain(' • pnpm native:android (run on Android)');
601
+ log.plain(" 2. Start developing:");
602
+ if (platforms.includes("native")) {
603
+ log.plain(" • pnpm dev:native (start native dev server)");
604
+ log.plain(" • pnpm native:ios (run on iOS)");
605
+ log.plain(" • pnpm native:android (run on Android)");
487
606
  }
488
- if (platforms.includes('web')) {
489
- log.plain(' • pnpm dev:web (start web dev server)');
490
- log.plain(' • pnpm web:build (build for production)');
607
+ if (platforms.includes("web")) {
608
+ log.plain(" • pnpm dev:web (start web dev server)");
609
+ log.plain(" • pnpm web:build (build for production)");
491
610
  }
492
611
  }
493
612
  else {
494
613
  if (setupScriptExists) {
495
- log.plain(' 2. Run starter setup (if not done):');
614
+ log.plain(" 2. Run starter setup (if not done):");
496
615
  log.plain(` cd ${projectName}/apps/native`);
497
- log.plain(' node scripts/starter-setup.mjs');
616
+ log.plain(" node scripts/starter-setup.mjs");
498
617
  }
499
- log.plain(` ${setupScriptExists ? '3' : '2'}. Start developing:`);
500
- if (platforms.includes('native')) {
501
- log.plain(' • pnpm dev:native (start native dev server)');
502
- log.plain(' • pnpm native:ios (run on iOS)');
503
- log.plain(' • pnpm native:android (run on Android)');
618
+ log.plain(` ${setupScriptExists ? "3" : "2"}. Start developing:`);
619
+ if (platforms.includes("native")) {
620
+ log.plain(" • pnpm dev:native (start native dev server)");
621
+ log.plain(" • pnpm native:ios (run on iOS)");
622
+ log.plain(" • pnpm native:android (run on Android)");
504
623
  }
505
- if (platforms.includes('web')) {
506
- log.plain(' • pnpm dev:web (start web dev server)');
507
- log.plain(' • pnpm web:build (build for production)');
624
+ if (platforms.includes("web")) {
625
+ log.plain(" • pnpm dev:web (start web dev server)");
626
+ log.plain(" • pnpm web:build (build for production)");
508
627
  }
509
628
  }
510
- log.plain('');
511
- log.info('Add features:');
512
- log.plain(' • vf list # See available features');
513
- if (platforms.includes('native')) {
514
- log.plain(' • vf add chatbot # Add native feature');
629
+ log.plain("");
630
+ log.info("Add features:");
631
+ log.plain(" • vf list # See available features");
632
+ if (platforms.includes("native")) {
633
+ log.plain(" • vf add chatbot # Add native feature");
515
634
  }
516
- if (platforms.includes('web')) {
517
- log.plain(' • vf add quiz --target web # Add web feature');
635
+ if (platforms.includes("web")) {
636
+ log.plain(" • vf add quiz --target web # Add web feature");
518
637
  }
519
- log.plain('');
520
- log.info('📚 Documentation: https://vibefast.pro/docs');
521
- log.info('💬 Support: support@vibefast.pro');
522
- log.plain('');
638
+ log.plain("");
639
+ log.info("📚 Documentation: https://vibefast.pro/docs");
640
+ log.info("💬 Support: support@vibefast.pro");
641
+ log.plain("");
523
642
  }
524
643
  catch (error) {
525
- log.plain('');
644
+ log.plain("");
526
645
  log.error(`Initialization failed: ${error.message}`);
527
- if (error.message.includes('git')) {
528
- log.plain('');
529
- log.info('Make sure Git is installed:');
530
- log.plain(' • macOS: brew install git');
531
- log.plain(' • Windows: https://git-scm.com/download/win');
532
- log.plain(' • Linux: sudo apt install git');
533
- }
534
- if (error.message.includes('EACCES') || error.message.includes('permission')) {
535
- log.plain('');
536
- log.info('Permission denied. Try:');
537
- log.plain(' • Run without sudo');
538
- log.plain('Check directory permissions');
539
- }
540
- log.plain('');
646
+ if (error.message.includes("git")) {
647
+ log.plain("");
648
+ log.info("Make sure Git is installed:");
649
+ log.plain(" • macOS: brew install git");
650
+ log.plain(" • Windows: https://git-scm.com/download/win");
651
+ log.plain(" • Linux: sudo apt install git");
652
+ }
653
+ if (error.message.includes("EACCES") ||
654
+ error.message.includes("permission")) {
655
+ log.plain("");
656
+ log.info("Permission denied. Try:");
657
+ log.plain("Run without sudo");
658
+ log.plain(" • Check directory permissions");
659
+ }
660
+ log.plain("");
541
661
  process.exit(1);
542
662
  }
543
663
  });