vibefast-cli 1.2.1 → 1.3.1

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 (348) hide show
  1. package/README.md +30 -95
  2. package/dist/__tests__/recipes.test.js +94 -91
  3. package/dist/__tests__/recipes.test.js.map +1 -1
  4. package/dist/commands/add.d.ts.map +1 -1
  5. package/dist/commands/add.js +301 -125
  6. package/dist/commands/add.js.map +1 -1
  7. package/dist/commands/checklist.d.ts.map +1 -1
  8. package/dist/commands/checklist.js +85 -44
  9. package/dist/commands/checklist.js.map +1 -1
  10. package/dist/commands/health.d.ts.map +1 -1
  11. package/dist/commands/health.js +13 -4
  12. package/dist/commands/health.js.map +1 -1
  13. package/dist/commands/init.d.ts.map +1 -1
  14. package/dist/commands/init.js +118 -26
  15. package/dist/commands/init.js.map +1 -1
  16. package/dist/commands/migrate.d.ts +3 -0
  17. package/dist/commands/migrate.d.ts.map +1 -0
  18. package/dist/commands/migrate.js +202 -0
  19. package/dist/commands/migrate.js.map +1 -0
  20. package/dist/commands/remove.d.ts.map +1 -1
  21. package/dist/commands/remove.js +61 -3
  22. package/dist/commands/remove.js.map +1 -1
  23. package/dist/core/auth.d.ts.map +1 -1
  24. package/dist/core/auth.js +20 -18
  25. package/dist/core/auth.js.map +1 -1
  26. package/dist/core/codemod.d.ts +33 -0
  27. package/dist/core/codemod.d.ts.map +1 -1
  28. package/dist/core/codemod.js +116 -0
  29. package/dist/core/codemod.js.map +1 -1
  30. package/dist/core/detect.d.ts.map +1 -1
  31. package/dist/core/detect.js +24 -7
  32. package/dist/core/detect.js.map +1 -1
  33. package/dist/core/journal.d.ts +1 -0
  34. package/dist/core/journal.d.ts.map +1 -1
  35. package/dist/core/journal.js.map +1 -1
  36. package/dist/core/recipes.d.ts.map +1 -1
  37. package/dist/core/recipes.js +25 -7
  38. package/dist/core/recipes.js.map +1 -1
  39. package/dist/index.js +2 -2
  40. package/dist/index.js.map +1 -1
  41. package/docs/architecture.md +50 -0
  42. package/docs/commands.md +78 -0
  43. package/docs/contributing.md +27 -0
  44. package/docs/quickstart.md +50 -0
  45. package/docs/recipes.md +57 -0
  46. package/docs/troubleshooting.md +31 -0
  47. package/package.json +2 -2
  48. package/recipes/0/apps/native/src/components/advanced-ui/timeline/demo.tsx +445 -0
  49. package/recipes/0/apps/native/src/components/advanced-ui/timeline/timeline-view.tsx +355 -0
  50. package/recipes/0/apps/native/src/components/advanced-ui/timeline/types.ts +31 -0
  51. package/recipes/0/recipe.json +18 -0
  52. package/recipes/animated-chip/apps/native/src/components/advanced-ui/chip/demo.tsx +2 -1
  53. package/recipes/animated-chip/recipe.json +5 -2
  54. package/recipes/animated-chip-native@latest.zip +0 -0
  55. package/recipes/animated-chip@latest.zip +0 -0
  56. package/recipes/animated-switch/apps/native/src/components/advanced-ui/switch/demo.tsx +1 -1
  57. package/recipes/animated-switch/recipe.json +5 -2
  58. package/recipes/animated-switch-native@latest.zip +0 -0
  59. package/recipes/animated-switch@latest.zip +0 -0
  60. package/recipes/audio-recorder/apps/native/src/features/audio-recorder/components/audio-recorder.tsx +2 -1
  61. package/recipes/audio-recorder/apps/native/src/features/audio-recorder/demo/with-recording-list-demo.tsx +2 -2
  62. package/recipes/audio-recorder/recipe.json +7 -2
  63. package/recipes/audio-recorder-native@latest.zip +0 -0
  64. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/components/audio-recorder.tsx +2 -1
  65. package/recipes/audio-recorder-supabase/apps/native/src/features/audio-recorder/demo/with-recording-list-demo.tsx +2 -1
  66. package/recipes/audio-recorder-supabase/recipe.json +12 -16
  67. package/recipes/audio-recorder-supabase-native@latest.zip +0 -0
  68. package/recipes/audio-recorder-supabase@latest.zip +0 -0
  69. package/recipes/audio-recorder@latest.zip +0 -0
  70. package/recipes/charts/apps/native/src/app/charts/index.tsx +3 -0
  71. package/recipes/charts/apps/native/src/features/charts/components/bar-chart.tsx +3 -1
  72. package/recipes/charts/apps/native/src/features/charts/components/candlestick-chart.tsx +3 -1
  73. package/recipes/charts/apps/native/src/features/charts/components/column-chart.tsx +3 -1
  74. package/recipes/charts/apps/native/src/features/charts/components/doughnut-chart.tsx +3 -1
  75. package/recipes/charts/apps/native/src/features/charts/components/line-chart.tsx +3 -1
  76. package/recipes/charts/apps/native/src/features/charts/components/radar-chart.tsx +3 -1
  77. package/recipes/charts/apps/native/src/features/charts/components/stacked-bar-chart.tsx +3 -1
  78. package/recipes/charts/recipe.json +13 -4
  79. package/recipes/charts-native@latest.zip +0 -0
  80. package/recipes/charts@latest.zip +0 -0
  81. package/recipes/chatbot/apps/native/src/features/chatbot/components/chat-markdown.tsx +86 -86
  82. package/recipes/chatbot/apps/native/src/features/chatbot/components/markdown/code-block.tsx +4 -4
  83. package/recipes/chatbot/recipe.json +3 -40
  84. package/recipes/chatbot-native@latest.zip +0 -0
  85. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/chat-markdown.tsx +4 -1
  86. package/recipes/chatbot-supabase/apps/native/src/features/chatbot/components/markdown/code-block.tsx +86 -53
  87. package/recipes/chatbot-supabase/recipe.json +3 -42
  88. package/recipes/chatbot-supabase-native@latest.zip +0 -0
  89. package/recipes/chatbot-supabase@latest.zip +0 -0
  90. package/recipes/chatbot@latest.zip +0 -0
  91. package/recipes/glowing-button/recipe.json +6 -2
  92. package/recipes/glowing-button-native@latest.zip +0 -0
  93. package/recipes/glowing-button@latest.zip +0 -0
  94. package/recipes/image-analysis/apps/native/src/app/analysis/[type]/_layout.tsx +5 -0
  95. package/recipes/image-analysis/apps/native/src/app/analysis/[type]/analysis-options.tsx +50 -0
  96. package/recipes/image-analysis/apps/native/src/app/analysis/[type]/camera.tsx +2 -0
  97. package/recipes/image-analysis/apps/native/src/app/analysis/[type]/index.tsx +50 -0
  98. package/recipes/image-analysis/apps/native/src/app/analysis/[type]/loading.tsx +50 -0
  99. package/recipes/image-analysis/apps/native/src/app/analysis/[type]/results.tsx +2 -0
  100. package/recipes/image-analysis/apps/native/src/app/analysis/[type]/trait-details.tsx +3 -0
  101. package/recipes/image-analysis/apps/native/src/features/image-analyzer/app/analysis-options-screen.tsx +2 -2
  102. package/recipes/image-analysis/apps/native/src/features/image-analyzer/app/camera.tsx +72 -65
  103. package/recipes/image-analysis/apps/native/src/features/image-analyzer/app/image-capture-screen.tsx +65 -47
  104. package/recipes/image-analysis/apps/native/src/features/image-analyzer/app/loading-screen.tsx +43 -2
  105. package/recipes/image-analysis/apps/native/src/features/image-analyzer/app/loading.tsx +34 -1
  106. package/recipes/image-analysis/apps/native/src/features/image-analyzer/hooks/use-image-analysis.ts +83 -2
  107. package/recipes/image-analysis/recipe.json +11 -19
  108. package/recipes/image-analysis-native@latest.zip +0 -0
  109. package/recipes/image-analysis-supabase/apps/native/src/app/analysis/[type]/_layout.tsx +5 -0
  110. package/recipes/image-analysis-supabase/apps/native/src/app/analysis/[type]/analysis-options.tsx +50 -0
  111. package/recipes/image-analysis-supabase/apps/native/src/app/analysis/[type]/camera.tsx +2 -0
  112. package/recipes/image-analysis-supabase/apps/native/src/app/analysis/[type]/index.tsx +50 -0
  113. package/recipes/image-analysis-supabase/apps/native/src/app/analysis/[type]/loading.tsx +50 -0
  114. package/recipes/image-analysis-supabase/apps/native/src/app/analysis/[type]/results.tsx +2 -0
  115. package/recipes/image-analysis-supabase/apps/native/src/app/analysis/[type]/trait-details.tsx +3 -0
  116. package/recipes/image-analysis-supabase/recipe.json +10 -37
  117. package/recipes/image-analysis-supabase-native@latest.zip +0 -0
  118. package/recipes/image-analysis-supabase@latest.zip +0 -0
  119. package/recipes/image-analysis@latest.zip +0 -0
  120. package/recipes/image-analyzer/apps/native/src/app/(root)/(protected)/image-analyzer/index.tsx +2 -0
  121. package/recipes/image-generator/apps/native/src/app/image-generator/gallery.tsx +3 -0
  122. package/recipes/image-generator/apps/native/src/app/image-generator/index.tsx +3 -0
  123. package/recipes/image-generator/recipe.json +8 -18
  124. package/recipes/image-generator-native@latest.zip +0 -0
  125. package/recipes/image-generator-supabase/recipe.json +6 -35
  126. package/recipes/image-generator-supabase-native@latest.zip +0 -0
  127. package/recipes/image-generator-supabase@latest.zip +0 -0
  128. package/recipes/image-generator@latest.zip +0 -0
  129. package/recipes/ios-widget/recipe.json +18 -119
  130. package/recipes/ios-widget-native@latest.zip +0 -0
  131. package/recipes/ios-widget@latest.zip +0 -0
  132. package/recipes/number-stepper/apps/native/src/components/advanced-ui/stepper/demo.tsx +1 -1
  133. package/recipes/number-stepper/recipe.json +5 -2
  134. package/recipes/number-stepper-native@latest.zip +0 -0
  135. package/recipes/number-stepper@latest.zip +0 -0
  136. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/interactive-onboarding.tsx +11 -18
  137. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/ai-tone-step.tsx +5 -7
  138. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/currency-step.tsx +9 -7
  139. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/feature-ai-step.tsx +8 -7
  140. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/feature-chatbot-step.tsx +6 -5
  141. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/feature-manual-step.tsx +4 -3
  142. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/feature-scan-step.tsx +6 -5
  143. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/main-reason-step.tsx +5 -7
  144. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/notification-step.tsx +7 -6
  145. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/overspend-step.tsx +5 -7
  146. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/personalizing-step.tsx +8 -7
  147. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/rating-step.tsx +6 -5
  148. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/reminder-step.tsx +5 -6
  149. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/safety-step.tsx +5 -4
  150. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/struggle-step.tsx +5 -7
  151. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/steps/welcome-step.tsx +7 -6
  152. package/recipes/onboarding/apps/native/src/features/onboarding/expense-tracker/components/ui/onboarding-header.tsx +4 -3
  153. package/recipes/onboarding/recipe.json +9 -6
  154. package/recipes/onboarding-native@latest.zip +0 -0
  155. package/recipes/onboarding@latest.zip +0 -0
  156. package/recipes/payments/apps/native/src/app/paywall/index.tsx +74 -0
  157. package/recipes/payments/apps/native/src/app/paywall/local.tsx +25 -0
  158. package/recipes/payments/apps/native/src/app/paywall/remote.tsx +23 -0
  159. package/recipes/payments/packages/backend/convex/payments.ts +21 -3
  160. package/recipes/payments/recipe.json +14 -34
  161. package/recipes/payments-native@latest.zip +0 -0
  162. package/recipes/payments-supabase/apps/native/src/app/paywall/index.tsx +74 -0
  163. package/recipes/payments-supabase/apps/native/src/app/paywall/local.tsx +25 -0
  164. package/recipes/payments-supabase/apps/native/src/app/paywall/remote.tsx +23 -0
  165. package/recipes/payments-supabase/recipe.json +16 -23
  166. package/recipes/payments-supabase-native@latest.zip +0 -0
  167. package/recipes/payments-supabase@latest.zip +0 -0
  168. package/recipes/payments@latest.zip +0 -0
  169. package/recipes/posthog/apps/native/src/components/analytics/navigation-tracker.tsx +14 -0
  170. package/recipes/posthog/apps/native/src/lib/hooks/use-navigation-analytics.ts +44 -0
  171. package/recipes/posthog/apps/native/src/providers/posthog-provider.tsx +51 -0
  172. package/recipes/posthog/recipe.json +60 -0
  173. package/recipes/posthog-native@latest.zip +0 -0
  174. package/recipes/progress-circle/apps/native/src/components/advanced-ui/progress-bars/progress-circle-page.tsx +1 -1
  175. package/recipes/progress-circle/recipe.json +5 -2
  176. package/recipes/progress-circle-native@latest.zip +0 -0
  177. package/recipes/progress-circle@latest.zip +0 -0
  178. package/recipes/quiz/apps/native/src/app/quiz/index.tsx +47 -0
  179. package/recipes/quiz/recipe.json +9 -6
  180. package/recipes/quiz-native@latest.zip +0 -0
  181. package/recipes/quiz@latest.zip +0 -0
  182. package/recipes/screen-kits/apps/native/src/app/screen-kits/_layout.tsx +12 -0
  183. package/recipes/screen-kits/apps/native/src/app/screen-kits/index.tsx +114 -0
  184. package/recipes/screen-kits/apps/native/src/features/screen-kits/index.ts +1 -0
  185. package/recipes/screen-kits/apps/native/src/features/screen-kits/types.ts +28 -0
  186. package/recipes/screen-kits/recipe.json +26 -0
  187. package/recipes/screen-kits-duolingo/apps/native/src/app/screen-kits/duolingo/_layout.tsx +12 -0
  188. package/recipes/screen-kits-duolingo/apps/native/src/app/screen-kits/duolingo/home.tsx +5 -0
  189. package/recipes/screen-kits-duolingo/apps/native/src/app/screen-kits/duolingo/index.tsx +5 -0
  190. package/recipes/screen-kits-duolingo/apps/native/src/app/screen-kits/duolingo/lesson-complete.tsx +5 -0
  191. package/recipes/screen-kits-duolingo/apps/native/src/app/screen-kits/duolingo/lesson-fail.tsx +5 -0
  192. package/recipes/screen-kits-duolingo/apps/native/src/app/screen-kits/duolingo/lesson.tsx +5 -0
  193. package/recipes/screen-kits-duolingo/apps/native/src/app/screen-kits/duolingo/skill-tree.tsx +5 -0
  194. package/recipes/screen-kits-duolingo/apps/native/src/features/screen-kits/duolingo/components/duo-button.tsx +174 -0
  195. package/recipes/screen-kits-duolingo/apps/native/src/features/screen-kits/duolingo/components/skill-button.tsx +186 -0
  196. package/recipes/screen-kits-duolingo/apps/native/src/features/screen-kits/duolingo/components/xp-header.tsx +115 -0
  197. package/recipes/screen-kits-duolingo/apps/native/src/features/screen-kits/duolingo/constants.ts +89 -0
  198. package/recipes/screen-kits-duolingo/apps/native/src/features/screen-kits/duolingo/index.ts +3 -0
  199. package/recipes/screen-kits-duolingo/apps/native/src/features/screen-kits/duolingo/screens/home-screen.tsx +225 -0
  200. package/recipes/screen-kits-duolingo/apps/native/src/features/screen-kits/duolingo/screens/lesson-complete-screen.tsx +485 -0
  201. package/recipes/screen-kits-duolingo/apps/native/src/features/screen-kits/duolingo/screens/lesson-fail-screen.tsx +105 -0
  202. package/recipes/screen-kits-duolingo/apps/native/src/features/screen-kits/duolingo/screens/lesson-screen.tsx +384 -0
  203. package/recipes/screen-kits-duolingo/recipe.json +58 -0
  204. package/recipes/screen-kits-duolingo-native@latest.zip +0 -0
  205. package/recipes/screen-kits-finance/apps/native/src/app/screen-kits/finance/_layout.tsx +45 -0
  206. package/recipes/screen-kits-finance/apps/native/src/app/screen-kits/finance/asset-detail.tsx +3 -0
  207. package/recipes/screen-kits-finance/apps/native/src/app/screen-kits/finance/notifications.tsx +3 -0
  208. package/recipes/screen-kits-finance/apps/native/src/app/screen-kits/finance/receive.tsx +3 -0
  209. package/recipes/screen-kits-finance/apps/native/src/app/screen-kits/finance/send.tsx +3 -0
  210. package/recipes/screen-kits-finance/apps/native/src/app/screen-kits/finance/swap.tsx +3 -0
  211. package/recipes/screen-kits-finance/apps/native/src/app/screen-kits/finance/wallet.tsx +3 -0
  212. package/recipes/screen-kits-finance/apps/native/src/features/screen-kits/finance/components/ActionButtons.tsx +78 -0
  213. package/recipes/screen-kits-finance/apps/native/src/features/screen-kits/finance/components/AssetRow.tsx +94 -0
  214. package/recipes/screen-kits-finance/apps/native/src/features/screen-kits/finance/components/BalanceCard.tsx +118 -0
  215. package/recipes/screen-kits-finance/apps/native/src/features/screen-kits/finance/constants.ts +85 -0
  216. package/recipes/screen-kits-finance/apps/native/src/features/screen-kits/finance/screens/asset-detail.tsx +378 -0
  217. package/recipes/screen-kits-finance/apps/native/src/features/screen-kits/finance/screens/notifications.tsx +210 -0
  218. package/recipes/screen-kits-finance/apps/native/src/features/screen-kits/finance/screens/receive-modal.tsx +317 -0
  219. package/recipes/screen-kits-finance/apps/native/src/features/screen-kits/finance/screens/send-modal.tsx +420 -0
  220. package/recipes/screen-kits-finance/apps/native/src/features/screen-kits/finance/screens/swap-modal.tsx +363 -0
  221. package/recipes/screen-kits-finance/apps/native/src/features/screen-kits/finance/screens/wallet-dashboard.tsx +281 -0
  222. package/recipes/screen-kits-finance/recipe.json +46 -0
  223. package/recipes/screen-kits-finance-native@latest.zip +0 -0
  224. package/recipes/screen-kits-fitness/apps/native/assets/sounds/timer-beep.wav +0 -0
  225. package/recipes/screen-kits-fitness/apps/native/src/app/screen-kits/fitness/_layout.tsx +10 -0
  226. package/recipes/screen-kits-fitness/apps/native/src/app/screen-kits/fitness/index.tsx +6 -0
  227. package/recipes/screen-kits-fitness/apps/native/src/app/screen-kits/fitness/timer.tsx +3 -0
  228. package/recipes/screen-kits-fitness/apps/native/src/app/screen-kits/fitness/workout.tsx +3 -0
  229. package/recipes/screen-kits-fitness/apps/native/src/features/screen-kits/fitness/components/timer-components.tsx +500 -0
  230. package/recipes/screen-kits-fitness/apps/native/src/features/screen-kits/fitness/components/timer-settings-modal.tsx +352 -0
  231. package/recipes/screen-kits-fitness/apps/native/src/features/screen-kits/fitness/components/workout-card.tsx +105 -0
  232. package/recipes/screen-kits-fitness/apps/native/src/features/screen-kits/fitness/constants.ts +189 -0
  233. package/recipes/screen-kits-fitness/apps/native/src/features/screen-kits/fitness/hooks/use-timer.ts +307 -0
  234. package/recipes/screen-kits-fitness/apps/native/src/features/screen-kits/fitness/index.ts +1 -0
  235. package/recipes/screen-kits-fitness/apps/native/src/features/screen-kits/fitness/screens/timer-screen.tsx +278 -0
  236. package/recipes/screen-kits-fitness/apps/native/src/features/screen-kits/fitness/screens/workout-dashboard.tsx +350 -0
  237. package/recipes/screen-kits-fitness/recipe.json +63 -0
  238. package/recipes/screen-kits-fitness-native@latest.zip +0 -0
  239. package/recipes/screen-kits-habits/apps/native/src/app/screen-kits/productivity/habits.tsx +1 -0
  240. package/recipes/screen-kits-habits/apps/native/src/app/screen-kits/productivity/kanban.tsx +1 -0
  241. package/recipes/screen-kits-habits/apps/native/src/app/screen-kits/productivity/routes.ts +4 -0
  242. package/recipes/screen-kits-habits/apps/native/src/features/screen-kits/productivity/components/AddTaskModal.tsx +246 -0
  243. package/recipes/screen-kits-habits/apps/native/src/features/screen-kits/productivity/components/DraggableTaskCard.tsx +92 -0
  244. package/recipes/screen-kits-habits/apps/native/src/features/screen-kits/productivity/components/KanbanColumn.tsx +238 -0
  245. package/recipes/screen-kits-habits/apps/native/src/features/screen-kits/productivity/components/TaskCard.tsx +144 -0
  246. package/recipes/screen-kits-habits/apps/native/src/features/screen-kits/productivity/components/add-habit-modal.tsx +271 -0
  247. package/recipes/screen-kits-habits/apps/native/src/features/screen-kits/productivity/constants.ts +295 -0
  248. package/recipes/screen-kits-habits/apps/native/src/features/screen-kits/productivity/kanban-utils.ts +62 -0
  249. package/recipes/screen-kits-habits/apps/native/src/features/screen-kits/productivity/screens/habit-tracker.tsx +1160 -0
  250. package/recipes/screen-kits-habits/apps/native/src/features/screen-kits/productivity/screens/kanban-board.tsx +432 -0
  251. package/recipes/screen-kits-habits/recipe.json +52 -0
  252. package/recipes/screen-kits-habits-native@latest.zip +0 -0
  253. package/recipes/screen-kits-native@latest.zip +0 -0
  254. package/recipes/sentry/apps/native/src/providers/sentry-provider.tsx +64 -0
  255. package/recipes/sentry/recipe.json +39 -0
  256. package/recipes/sentry-native@latest.zip +0 -0
  257. package/recipes/swipe-slider/apps/native/src/components/advanced-ui/sliders/swipe-slider-page.tsx +1 -1
  258. package/recipes/swipe-slider/recipe.json +5 -2
  259. package/recipes/swipe-slider-native@latest.zip +0 -0
  260. package/recipes/swipe-slider@latest.zip +0 -0
  261. package/recipes/timeline/apps/native/src/components/advanced-ui/timeline/demo.tsx +2 -1
  262. package/recipes/timeline/recipe.json +5 -2
  263. package/recipes/timeline-native@latest.zip +0 -0
  264. package/recipes/timeline@latest.zip +0 -0
  265. package/recipes/tracker-app/apps/native/src/app/tracker-app/index.tsx +1 -0
  266. package/recipes/tracker-app/recipe.json +10 -7
  267. package/recipes/tracker-app-native@latest.zip +0 -0
  268. package/recipes/tracker-app@latest.zip +0 -0
  269. package/recipes/upload-all.sh +8 -31
  270. package/recipes/voice-bot/apps/native/src/app/voice-bot/index.tsx +56 -0
  271. package/recipes/voice-bot/recipe.json +31 -7
  272. package/recipes/voice-bot-native@latest.zip +0 -0
  273. package/recipes/voice-bot@latest.zip +0 -0
  274. package/recipes/wake-word/apps/native/src/app/{(root)/(protected)/test-wake-word.tsx → test-wake-word.tsx} +43 -4
  275. package/recipes/wake-word/recipe.json +16 -26
  276. package/recipes/wake-word-native@latest.zip +0 -0
  277. package/recipes/wake-word@latest.zip +0 -0
  278. package/scripts/create-advanced-ui-recipes.sh +46 -19
  279. package/scripts/create-recipes.mjs +471 -117
  280. package/scripts/package-recipes.mjs +76 -0
  281. package/scripts/publish-all.sh +6 -2
  282. package/CHANGELOG.md +0 -198
  283. package/docs/archive/AUTO-DETECT-DEPS.md +0 -607
  284. package/docs/archive/FINAL-PACKAGE-STRATEGY.md +0 -583
  285. package/docs/archive/FINAL-SIMPLE-PLAN.md +0 -487
  286. package/docs/archive/FINAL-STATUS.md +0 -144
  287. package/docs/archive/FLOW-DIAGRAM.md +0 -1629
  288. package/docs/archive/GOTCHAS-AND-RISKS.md +0 -801
  289. package/docs/archive/IMPLEMENTATION-PLAN.md +0 -1360
  290. package/docs/archive/PLAN.md +0 -453
  291. package/docs/archive/PRODUCTION-READINESS.md +0 -684
  292. package/docs/archive/PRODUCTION-TEST-RESULTS.md +0 -465
  293. package/docs/archive/SIMPLIFIED-PLAN.md +0 -578
  294. package/docs/archive/STATUS.md +0 -199
  295. package/docs/archive/SUCCESS.md +0 -259
  296. package/docs/archive/TEST-SUMMARY.md +0 -261
  297. package/docs/archive/TESTING-CHECKLIST.md +0 -450
  298. package/docs/archive/USER-MODIFICATIONS.md +0 -448
  299. package/docs/decisions.md +0 -55
  300. package/docs/manual-testing.md +0 -91
  301. package/docs/next-steps.md +0 -12
  302. package/recipes/README.md +0 -156
  303. package/recipes/audio-recorder-supabase/packages/backend/src/services/recordings.ts +0 -369
  304. package/recipes/chatbot/apps/native/src/api-client/chatbot.ts +0 -83
  305. package/recipes/chatbot/packages/backend/convex/agents.ts +0 -115
  306. package/recipes/chatbot/packages/backend/convex/tools/index.ts +0 -18
  307. package/recipes/chatbot/packages/backend/convex/tools/knowledgeRetrieval.ts +0 -97
  308. package/recipes/chatbot/packages/backend/convex/tools/tavilySearch.ts +0 -83
  309. package/recipes/chatbot/packages/backend/convex/tools/userProfile.ts +0 -72
  310. package/recipes/chatbot-supabase/apps/native/src/api-client/supabase/chatbot.ts +0 -515
  311. package/recipes/chatbot-supabase/packages/backend/src/services/conversations.ts +0 -243
  312. package/recipes/chatbot-supabase/packages/backend/src/services/messages.ts +0 -327
  313. package/recipes/image-analysis/apps/native/src/api-client/image-analyzer.ts +0 -62
  314. package/recipes/image-analysis-supabase/packages/backend/src/services/image-analyses.ts +0 -132
  315. package/recipes/image-generator/apps/native/src/api-client/image-generator.ts +0 -34
  316. package/recipes/payments/apps/native/src/api-client/payments.ts +0 -44
  317. package/recipes/payments-supabase/packages/backend/src/services/payments.ts +0 -201
  318. package/recipes/posthog.json +0 -47
  319. package/recipes/revenuecat.json +0 -43
  320. package/recipes/sentry.json +0 -47
  321. package/recipes/wake-word/apps/native/assets/vosk-model/README.md +0 -103
  322. package/recipes/wake-word/apps/native/scripts/download-vosk-model.mjs +0 -127
  323. /package/recipes/{audio-recorder/apps/native/src/app/(root)/(protected) → audio-recorder-supabase/apps/native/src/app}/audio-recorder/index.tsx +0 -0
  324. /package/recipes/ios-widget/{targets → apps/native/targets}/widget/AppIntent.swift +0 -0
  325. /package/recipes/ios-widget/{targets → apps/native/targets}/widget/Assets.xcassets/AppIcon.appiconset/App-Icon-20x20@1x.png +0 -0
  326. /package/recipes/ios-widget/{targets → apps/native/targets}/widget/Assets.xcassets/AppIcon.appiconset/App-Icon-20x20@2x.png +0 -0
  327. /package/recipes/ios-widget/{targets → apps/native/targets}/widget/Assets.xcassets/AppIcon.appiconset/App-Icon-20x20@3x.png +0 -0
  328. /package/recipes/ios-widget/{targets → apps/native/targets}/widget/Assets.xcassets/AppIcon.appiconset/App-Icon-29x29@1x.png +0 -0
  329. /package/recipes/ios-widget/{targets → apps/native/targets}/widget/Assets.xcassets/AppIcon.appiconset/App-Icon-29x29@2x.png +0 -0
  330. /package/recipes/ios-widget/{targets → apps/native/targets}/widget/Assets.xcassets/AppIcon.appiconset/App-Icon-29x29@3x.png +0 -0
  331. /package/recipes/ios-widget/{targets → apps/native/targets}/widget/Assets.xcassets/AppIcon.appiconset/App-Icon-40x40@1x.png +0 -0
  332. /package/recipes/ios-widget/{targets → apps/native/targets}/widget/Assets.xcassets/AppIcon.appiconset/App-Icon-40x40@2x.png +0 -0
  333. /package/recipes/ios-widget/{targets → apps/native/targets}/widget/Assets.xcassets/AppIcon.appiconset/App-Icon-40x40@3x.png +0 -0
  334. /package/recipes/ios-widget/{targets → apps/native/targets}/widget/Assets.xcassets/AppIcon.appiconset/App-Icon-60x60@2x.png +0 -0
  335. /package/recipes/ios-widget/{targets → apps/native/targets}/widget/Assets.xcassets/AppIcon.appiconset/App-Icon-60x60@3x.png +0 -0
  336. /package/recipes/ios-widget/{targets → apps/native/targets}/widget/Assets.xcassets/AppIcon.appiconset/App-Icon-76x76@1x.png +0 -0
  337. /package/recipes/ios-widget/{targets → apps/native/targets}/widget/Assets.xcassets/AppIcon.appiconset/App-Icon-76x76@2x.png +0 -0
  338. /package/recipes/ios-widget/{targets → apps/native/targets}/widget/Assets.xcassets/AppIcon.appiconset/App-Icon-83.5x83.5@2x.png +0 -0
  339. /package/recipes/ios-widget/{targets → apps/native/targets}/widget/Assets.xcassets/AppIcon.appiconset/Contents.json +0 -0
  340. /package/recipes/ios-widget/{targets → apps/native/targets}/widget/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png +0 -0
  341. /package/recipes/ios-widget/{targets → apps/native/targets}/widget/CalorieTrackerWidget.swift +0 -0
  342. /package/recipes/ios-widget/{targets → apps/native/targets}/widget/HabitTrackerWidget.swift +0 -0
  343. /package/recipes/ios-widget/{targets → apps/native/targets}/widget/Info.plist +0 -0
  344. /package/recipes/ios-widget/{targets → apps/native/targets}/widget/WidgetLiveActivity.swift +0 -0
  345. /package/recipes/ios-widget/{targets → apps/native/targets}/widget/expo-target.config.js +0 -0
  346. /package/recipes/ios-widget/{targets → apps/native/targets}/widget/generated.entitlements +0 -0
  347. /package/recipes/ios-widget/{targets → apps/native/targets}/widget/index.swift +0 -0
  348. /package/recipes/ios-widget/{targets → apps/native/targets}/widget/widgets.swift +0 -0
@@ -1,1360 +0,0 @@
1
- # 🔧 Technical Implementation Plan
2
-
3
- ## Overview
4
-
5
- This is the **exact technical plan** for making VibeFast CLI production-ready. No hand-waving, just concrete steps with code examples and gotchas.
6
-
7
- ---
8
-
9
- ## Phase 1: Interactive Confirmation (Priority 1) 🔴
10
-
11
- ### Goal
12
- When files exist, ask user y/n before overwriting. Show what will be lost.
13
-
14
- ### Technical Approach
15
-
16
- #### Step 1.1: Add readline-sync for prompts
17
-
18
- **Why not built-in readline?**
19
- - Built-in readline is async and complex
20
- - readline-sync is simpler for CLI prompts
21
- - Works in both interactive and non-interactive modes
22
-
23
- ```bash
24
- cd vibefast-cli
25
- npm install readline-sync
26
- npm install --save-dev @types/readline-sync
27
- ```
28
-
29
- #### Step 1.2: Create prompt utility
30
-
31
- **File:** `src/core/prompt.ts`
32
-
33
- ```typescript
34
- import readlineSync from 'readline-sync';
35
-
36
- export function promptUser(question: string): string {
37
- // Check if we're in a non-interactive environment (CI, piped input)
38
- if (!process.stdin.isTTY) {
39
- return 'n'; // Default to no in non-interactive mode
40
- }
41
-
42
- return readlineSync.question(question);
43
- }
44
-
45
- export function promptYesNo(question: string, defaultYes = false): boolean {
46
- const answer = promptUser(question);
47
- const normalized = answer.toLowerCase().trim();
48
-
49
- if (normalized === '') {
50
- return defaultYes;
51
- }
52
-
53
- return normalized === 'y' || normalized === 'yes';
54
- }
55
- ```
56
-
57
- **Gotcha:** CI environments don't have TTY, so we need to handle that.
58
-
59
- #### Step 1.3: Update copyTree to detect conflicts
60
-
61
- **File:** `src/core/fsx.ts`
62
-
63
- ```typescript
64
- export interface CopyResult {
65
- files: string[];
66
- conflicts: string[];
67
- skipped: string[];
68
- }
69
-
70
- export async function copyTree(
71
- src: string,
72
- dest: string,
73
- options?: {
74
- dryRun?: boolean;
75
- force?: boolean;
76
- interactive?: boolean;
77
- }
78
- ): Promise<CopyResult> {
79
- const copied: string[] = [];
80
- const conflicts: string[] = [];
81
- const skipped: string[] = [];
82
-
83
- async function copyRecursive(srcPath: string, destPath: string) {
84
- const stats = await stat(srcPath);
85
-
86
- if (stats.isDirectory()) {
87
- if (!options?.dryRun) {
88
- await ensureDir(destPath);
89
- }
90
- const entries = await readdir(srcPath);
91
- for (const entry of entries) {
92
- await copyRecursive(join(srcPath, entry), join(destPath, entry));
93
- }
94
- } else {
95
- const destExists = await exists(destPath);
96
-
97
- if (destExists) {
98
- conflicts.push(destPath);
99
-
100
- // If not force and not interactive, throw error
101
- if (!options?.force && !options?.interactive) {
102
- throw new Error(`File exists: ${destPath}. Use --force to overwrite.`);
103
- }
104
-
105
- // If interactive and not dry-run, ask user
106
- if (options?.interactive && !options?.dryRun) {
107
- const { promptYesNo } = await import('./prompt.js');
108
- const shouldOverwrite = promptYesNo(
109
- `Overwrite ${destPath}? (y/N): `,
110
- false
111
- );
112
-
113
- if (!shouldOverwrite) {
114
- skipped.push(destPath);
115
- return; // Skip this file
116
- }
117
- }
118
- }
119
-
120
- if (!options?.dryRun) {
121
- await ensureDir(dirname(destPath));
122
- await copyFile(srcPath, destPath);
123
- }
124
- copied.push(destPath);
125
- }
126
- }
127
-
128
- await copyRecursive(src, dest);
129
- return { files: copied, conflicts, skipped };
130
- }
131
- ```
132
-
133
-
134
- #### Step 1.4: Update add command to use interactive mode
135
-
136
- **File:** `src/commands/add.ts`
137
-
138
- ```typescript
139
- // After checking if already installed
140
- if (existing && !options.force) {
141
- log.warn(`${feature} is already installed for ${target}`);
142
- log.info('Use --force to reinstall');
143
- return;
144
- }
145
-
146
- // NEW: Scan for conflicts BEFORE downloading
147
- if (!options.dryRun && !options.force) {
148
- log.info('Checking for file conflicts...');
149
-
150
- // We need to know what files will be copied
151
- // Problem: We don't have the recipe yet!
152
- // Solution: Download recipe first, check conflicts, then ask
153
- }
154
-
155
- // Copy files with interactive mode
156
- const result = await copyTree(srcPath, destPath, {
157
- dryRun: options.dryRun,
158
- force: options.force,
159
- interactive: !options.force && !options.dryRun, // Enable interactive if not force
160
- });
161
-
162
- // Show results
163
- if (result.conflicts.length > 0) {
164
- if (options.dryRun) {
165
- log.warn(`⚠ ${result.conflicts.length} files will be overwritten:`);
166
- result.conflicts.slice(0, 5).forEach(f => {
167
- const relativePath = f.replace(paths.cwd + '/', '');
168
- log.plain(` • ${relativePath}`);
169
- });
170
- if (result.conflicts.length > 5) {
171
- log.plain(` ... and ${result.conflicts.length - 5} more`);
172
- }
173
- log.warn('⚠ Make sure you have committed your changes to Git!');
174
- }
175
- }
176
-
177
- if (result.skipped.length > 0) {
178
- log.info(`ℹ Skipped ${result.skipped.length} files (user chose not to overwrite)`);
179
- }
180
-
181
- copiedFiles.push(...result.files);
182
- ```
183
-
184
- **Gotcha:** We need the recipe to know what files will be copied, but we don't want to download it if user will cancel. Solution: Download first, show conflicts, then proceed.
185
-
186
- #### Step 1.5: Add --yes flag for automation
187
-
188
- **File:** `src/commands/add.ts`
189
-
190
- ```typescript
191
- export const addCommand = new Command('add')
192
- .description('Add a VibeFast feature to your project')
193
- .argument('<feature>', 'Feature name to install')
194
- .option('--target <target>', 'Target platform (native or web)', 'native')
195
- .option('--dry-run', 'Preview changes without applying')
196
- .option('--force', 'Overwrite existing files without asking')
197
- .option('--yes', 'Answer yes to all prompts (for automation)')
198
- .action(async (feature: string, options) => {
199
- // ...
200
- const interactive = !options.force && !options.dryRun && !options.yes;
201
- // ...
202
- });
203
- ```
204
-
205
- **Testing:**
206
- ```bash
207
- # Interactive mode (asks for each file)
208
- $ vf add charts
209
-
210
- # Force mode (overwrites all)
211
- $ vf add charts --force
212
-
213
- # Automation mode (assumes yes)
214
- $ vf add charts --yes
215
-
216
- # Dry run (shows what would happen)
217
- $ vf add charts --dry-run
218
- ```
219
-
220
- ---
221
-
222
- ## Phase 2: Package Dependency Management (Priority 2) 🔴
223
-
224
- ### Goal
225
- Detect required packages, show them to user, optionally install them.
226
-
227
- ### The Hard Part: How to Know What Packages Are Needed?
228
-
229
- **Option A: Parse imports from files** ❌
230
- ```typescript
231
- // Parse all .ts/.tsx files and extract imports
232
- import { Chart } from 'react-native-chart-kit'; // Need to detect this
233
- ```
234
- **Problems:**
235
- - Regex parsing is fragile
236
- - Need to parse TypeScript AST (complex)
237
- - Slow for large features
238
- - Can't detect dynamic imports
239
- - Can't determine versions
240
-
241
- **Option B: Add dependencies to recipe.json** ✅
242
- ```json
243
- {
244
- "name": "charts",
245
- "dependencies": {
246
- "npm": {
247
- "react-native-chart-kit": "^6.12.0",
248
- "react-native-svg": "^13.9.0"
249
- }
250
- }
251
- }
252
- ```
253
- **Advantages:**
254
- - Explicit and clear
255
- - Recipe creator knows exact versions
256
- - Fast (no parsing needed)
257
- - Can specify peer dependencies
258
- - Can add install instructions
259
-
260
- **Decision: Use Option B** - Recipe creators must specify dependencies manually.
261
-
262
- ### Technical Approach
263
-
264
- #### Step 2.1: Update recipe.json schema
265
-
266
- **File:** `src/commands/add.ts`
267
-
268
- ```typescript
269
- interface RecipeManifest {
270
- name: string;
271
- version: string;
272
- description: string;
273
- copy: Array<{ from: string; to: string }>;
274
- nav: { href: string; label: string };
275
- target: 'native' | 'web';
276
-
277
- // NEW: Dependencies
278
- dependencies?: {
279
- npm?: Record<string, string>;
280
- expo?: Record<string, string>;
281
- };
282
-
283
- // NEW: Post-install instructions
284
- postInstall?: {
285
- message?: string;
286
- commands?: string[];
287
- };
288
- }
289
- ```
290
-
291
- #### Step 2.2: Detect package manager
292
-
293
- **File:** `src/core/packageManager.ts`
294
-
295
- ```typescript
296
- import { exists } from './fsx.js';
297
- import { join } from 'path';
298
-
299
- export type PackageManager = 'npm' | 'yarn' | 'pnpm' | 'bun';
300
-
301
- export async function detectPackageManager(cwd: string): Promise<PackageManager> {
302
- // Check lock files
303
- if (await exists(join(cwd, 'yarn.lock'))) {
304
- return 'yarn';
305
- }
306
- if (await exists(join(cwd, 'pnpm-lock.yaml'))) {
307
- return 'pnpm';
308
- }
309
- if (await exists(join(cwd, 'bun.lockb'))) {
310
- return 'bun';
311
- }
312
-
313
- // Default to npm
314
- return 'npm';
315
- }
316
-
317
- export function getInstallCommand(
318
- pm: PackageManager,
319
- packages: string[]
320
- ): string {
321
- const pkgList = packages.join(' ');
322
-
323
- switch (pm) {
324
- case 'yarn':
325
- return `yarn add ${pkgList}`;
326
- case 'pnpm':
327
- return `pnpm add ${pkgList}`;
328
- case 'bun':
329
- return `bun add ${pkgList}`;
330
- default:
331
- return `npm install ${pkgList}`;
332
- }
333
- }
334
- ```
335
-
336
- **Gotcha:** Monorepos might have multiple package.json files. We need to install in the right workspace.
337
-
338
- #### Step 2.3: Install packages function
339
-
340
- **File:** `src/core/packageManager.ts`
341
-
342
- ```typescript
343
- import { executeBash } from './shell.js';
344
-
345
- export async function installPackages(
346
- packages: Record<string, string>,
347
- cwd: string
348
- ): Promise<{ success: boolean; error?: string }> {
349
- const pm = await detectPackageManager(cwd);
350
-
351
- // Convert to array of package@version
352
- const pkgList = Object.entries(packages).map(
353
- ([name, version]) => `${name}@${version}`
354
- );
355
-
356
- const command = getInstallCommand(pm, pkgList);
357
-
358
- try {
359
- log.info(`Running: ${command}`);
360
- const result = await executeBash(command, { cwd });
361
-
362
- if (result.exitCode !== 0) {
363
- return {
364
- success: false,
365
- error: result.stderr || 'Installation failed'
366
- };
367
- }
368
-
369
- return { success: true };
370
- } catch (error: any) {
371
- return {
372
- success: false,
373
- error: error.message
374
- };
375
- }
376
- }
377
- ```
378
-
379
- **File:** `src/core/shell.ts` (NEW)
380
-
381
- ```typescript
382
- import { exec } from 'child_process';
383
- import { promisify } from 'util';
384
-
385
- const execAsync = promisify(exec);
386
-
387
- export async function executeBash(
388
- command: string,
389
- options?: { cwd?: string }
390
- ): Promise<{ stdout: string; stderr: string; exitCode: number }> {
391
- try {
392
- const { stdout, stderr } = await execAsync(command, {
393
- cwd: options?.cwd,
394
- maxBuffer: 10 * 1024 * 1024, // 10MB buffer
395
- });
396
-
397
- return { stdout, stderr, exitCode: 0 };
398
- } catch (error: any) {
399
- return {
400
- stdout: error.stdout || '',
401
- stderr: error.stderr || '',
402
- exitCode: error.code || 1,
403
- };
404
- }
405
- }
406
- ```
407
-
408
-
409
- #### Step 2.4: Update add command to handle dependencies
410
-
411
- **File:** `src/commands/add.ts`
412
-
413
- ```typescript
414
- // After copying files and adding watermarks
415
-
416
- // Handle dependencies
417
- if (manifest.dependencies && !options.dryRun) {
418
- const npmDeps = manifest.dependencies.npm || {};
419
- const expoDeps = manifest.dependencies.expo || {};
420
- const allDeps = { ...npmDeps, ...expoDeps };
421
-
422
- if (Object.keys(allDeps).length > 0) {
423
- log.plain('');
424
- log.warn('⚠ This feature requires additional packages:');
425
- log.plain('');
426
-
427
- Object.entries(allDeps).forEach(([pkg, version]) => {
428
- log.plain(` • ${pkg}@${version}`);
429
- });
430
-
431
- log.plain('');
432
-
433
- // Detect package manager
434
- const pm = await detectPackageManager(paths.cwd);
435
- const pkgList = Object.entries(allDeps).map(
436
- ([name, version]) => `${name}@${version}`
437
- );
438
- const installCmd = getInstallCommand(pm, pkgList);
439
-
440
- log.info('📦 Install with:');
441
- log.plain(` ${installCmd}`);
442
- log.plain('');
443
-
444
- // Ask if user wants to install now
445
- if (!options.skipInstall && !options.yes) {
446
- const shouldInstall = promptYesNo(
447
- 'Install packages now? (Y/n): ',
448
- true // Default to yes
449
- );
450
-
451
- if (shouldInstall) {
452
- log.info('Installing packages...');
453
- const result = await installPackages(allDeps, paths.cwd);
454
-
455
- if (result.success) {
456
- log.success('✓ Packages installed successfully!');
457
- } else {
458
- log.error(`✗ Package installation failed: ${result.error}`);
459
- log.info('You can install them manually with:');
460
- log.plain(` ${installCmd}`);
461
- }
462
- }
463
- } else if (options.yes) {
464
- // Auto-install in --yes mode
465
- log.info('Installing packages...');
466
- const result = await installPackages(allDeps, paths.cwd);
467
-
468
- if (!result.success) {
469
- log.error(`✗ Package installation failed: ${result.error}`);
470
- process.exit(1);
471
- }
472
- }
473
- }
474
- }
475
-
476
- // Post-install instructions
477
- if (manifest.postInstall && !options.dryRun) {
478
- log.plain('');
479
- log.info('📝 Post-install steps:');
480
-
481
- if (manifest.postInstall.message) {
482
- log.plain(` ${manifest.postInstall.message}`);
483
- }
484
-
485
- if (manifest.postInstall.commands) {
486
- log.plain('');
487
- log.info('Run these commands:');
488
- manifest.postInstall.commands.forEach(cmd => {
489
- log.plain(` $ ${cmd}`);
490
- });
491
- }
492
- }
493
- ```
494
-
495
- #### Step 2.5: Add --skip-install flag
496
-
497
- ```typescript
498
- export const addCommand = new Command('add')
499
- .description('Add a VibeFast feature to your project')
500
- .argument('<feature>', 'Feature name to install')
501
- .option('--target <target>', 'Target platform (native or web)', 'native')
502
- .option('--dry-run', 'Preview changes without applying')
503
- .option('--force', 'Overwrite existing files without asking')
504
- .option('--yes', 'Answer yes to all prompts')
505
- .option('--skip-install', 'Skip package installation')
506
- .action(async (feature: string, options) => {
507
- // ...
508
- });
509
- ```
510
-
511
- **Testing:**
512
- ```bash
513
- # Interactive (asks to install)
514
- $ vf add charts
515
-
516
- # Auto-install
517
- $ vf add charts --yes
518
-
519
- # Skip install
520
- $ vf add charts --skip-install
521
-
522
- # Dry run (shows packages but doesn't install)
523
- $ vf add charts --dry-run
524
- ```
525
-
526
- **Gotcha:** Monorepo workspaces - need to detect if we should install in root or workspace.
527
-
528
- #### Step 2.6: Handle monorepo workspaces
529
-
530
- **File:** `src/core/packageManager.ts`
531
-
532
- ```typescript
533
- export async function findPackageJsonDir(startDir: string): Promise<string> {
534
- let currentDir = startDir;
535
-
536
- while (currentDir !== '/') {
537
- const pkgJsonPath = join(currentDir, 'package.json');
538
-
539
- if (await exists(pkgJsonPath)) {
540
- // Check if this is a workspace root or a workspace package
541
- const pkgJson = JSON.parse(await readFileContent(pkgJsonPath));
542
-
543
- // If it has workspaces, it's the root
544
- if (pkgJson.workspaces) {
545
- return currentDir;
546
- }
547
-
548
- // Otherwise, keep looking up
549
- }
550
-
551
- currentDir = dirname(currentDir);
552
- }
553
-
554
- // Fallback to start directory
555
- return startDir;
556
- }
557
-
558
- export async function installPackages(
559
- packages: Record<string, string>,
560
- cwd: string
561
- ): Promise<{ success: boolean; error?: string }> {
562
- // Find the correct directory to run install in
563
- const installDir = await findPackageJsonDir(cwd);
564
-
565
- const pm = await detectPackageManager(installDir);
566
-
567
- // ... rest of the function
568
- }
569
- ```
570
-
571
- ---
572
-
573
- ## Phase 3: Manual Steps Support (Priority 3) 🟡
574
-
575
- ### Goal
576
- For features like Sentry, PostHog, etc., show manual configuration steps.
577
-
578
- ### Technical Approach
579
-
580
- #### Step 3.1: Extend recipe.json schema
581
-
582
- ```typescript
583
- interface RecipeManifest {
584
- // ... existing fields
585
-
586
- // NEW: Environment variables needed
587
- env?: {
588
- required?: Array<{
589
- key: string;
590
- description: string;
591
- example: string;
592
- link?: string;
593
- }>;
594
- optional?: Array<{
595
- key: string;
596
- description: string;
597
- example: string;
598
- default?: string;
599
- }>;
600
- };
601
-
602
- // NEW: Manual steps
603
- manualSteps?: Array<{
604
- step: number;
605
- title: string;
606
- description: string;
607
- file?: string;
608
- action?: 'add' | 'modify' | 'create';
609
- content?: string;
610
- link?: string;
611
- command?: string;
612
- }>;
613
- }
614
- ```
615
-
616
- #### Step 3.2: Display manual steps
617
-
618
- **File:** `src/commands/add.ts`
619
-
620
- ```typescript
621
- // After installation completes
622
-
623
- if (manifest.manualSteps && !options.dryRun) {
624
- log.plain('');
625
- log.warn('⚠ MANUAL STEPS REQUIRED:');
626
- log.plain('');
627
- log.plain('This feature requires some manual configuration:');
628
- log.plain('');
629
-
630
- manifest.manualSteps.forEach(step => {
631
- log.plain('┌─────────────────────────────────────────────────────────────┐');
632
- log.plain(`│ Step ${step.step}: ${step.title.padEnd(52)} │`);
633
- log.plain('│ │');
634
-
635
- // Wrap description to fit in box
636
- const descLines = wrapText(step.description, 59);
637
- descLines.forEach(line => {
638
- log.plain(`│ ${line.padEnd(59)} │`);
639
- });
640
-
641
- if (step.file) {
642
- log.plain('│ │');
643
- log.plain(`│ 📝 File: ${step.file.padEnd(51)} │`);
644
- }
645
-
646
- if (step.content) {
647
- log.plain('│ │');
648
- log.plain(`│ Add: ${step.content.padEnd(54)} │`);
649
- }
650
-
651
- if (step.link) {
652
- log.plain('│ │');
653
- log.plain(`│ 🔗 ${step.link.padEnd(57)} │`);
654
- }
655
-
656
- if (step.command) {
657
- log.plain('│ │');
658
- log.plain(`│ $ ${step.command.padEnd(57)} │`);
659
- }
660
-
661
- log.plain('└─────────────────────────────────────────────────────────────┘');
662
- log.plain('');
663
- });
664
-
665
- log.info('💡 Tip: Run \'vf checklist ${manifest.name}\' to see these steps again.');
666
- }
667
-
668
- // Environment variables
669
- if (manifest.env?.required && !options.dryRun) {
670
- log.plain('');
671
- log.warn('⚠ REQUIRED ENVIRONMENT VARIABLES:');
672
- log.plain('');
673
-
674
- manifest.env.required.forEach(envVar => {
675
- log.plain(` ${envVar.key}`);
676
- log.plain(` ${envVar.description}`);
677
- log.plain(` Example: ${envVar.example}`);
678
- if (envVar.link) {
679
- log.plain(` Get it: ${envVar.link}`);
680
- }
681
- log.plain('');
682
- });
683
-
684
- log.info('Add these to your .env file');
685
- }
686
- ```
687
-
688
- **File:** `src/core/log.ts` (add helper)
689
-
690
- ```typescript
691
- export function wrapText(text: string, maxWidth: number): string[] {
692
- const words = text.split(' ');
693
- const lines: string[] = [];
694
- let currentLine = '';
695
-
696
- words.forEach(word => {
697
- if ((currentLine + word).length <= maxWidth) {
698
- currentLine += (currentLine ? ' ' : '') + word;
699
- } else {
700
- if (currentLine) lines.push(currentLine);
701
- currentLine = word;
702
- }
703
- });
704
-
705
- if (currentLine) lines.push(currentLine);
706
-
707
- return lines;
708
- }
709
- ```
710
-
711
- #### Step 3.3: Add vf checklist command
712
-
713
- **File:** `src/commands/checklist.ts` (NEW)
714
-
715
- ```typescript
716
- import { Command } from 'commander';
717
- import { log } from '../core/log.js';
718
- import { getPaths } from '../core/paths.js';
719
- import { getEntry } from '../core/journal.js';
720
-
721
- export const checklistCommand = new Command('checklist')
722
- .description('Show manual setup steps for an installed feature')
723
- .argument('<feature>', 'Feature name')
724
- .action(async (feature: string) => {
725
- try {
726
- const paths = getPaths();
727
-
728
- // Check if feature is installed
729
- const entry = await getEntry(paths.journalFile, feature, 'native');
730
- if (!entry) {
731
- log.error(`${feature} is not installed`);
732
- log.info('Run "vf list" to see installed features');
733
- process.exit(1);
734
- }
735
-
736
- // Problem: We don't have the recipe manifest in the journal!
737
- // Solution: Store it in the journal when installing
738
-
739
- if (!entry.manifest?.manualSteps) {
740
- log.info(`${feature} has no manual setup steps`);
741
- return;
742
- }
743
-
744
- // Display the steps (same code as in add.ts)
745
- log.info(`Manual setup steps for ${feature}:`);
746
- log.plain('');
747
-
748
- // ... display logic
749
-
750
- } catch (error: any) {
751
- log.error(`Failed to show checklist: ${error.message}`);
752
- process.exit(1);
753
- }
754
- });
755
- ```
756
-
757
- **Gotcha:** We need to store the manifest in the journal so we can show it later.
758
-
759
- #### Step 3.4: Update journal to store manifest
760
-
761
- **File:** `src/core/journal.ts`
762
-
763
- ```typescript
764
- export interface JournalEntry {
765
- feature: string;
766
- target: 'native' | 'web';
767
- files: string[];
768
- insertedNav: boolean;
769
- navHref?: string;
770
- navLabel?: string;
771
- ts: number;
772
-
773
- // NEW: Store manifest for later reference
774
- manifest?: {
775
- version: string;
776
- manualSteps?: any[];
777
- env?: any;
778
- postInstall?: any;
779
- };
780
- }
781
- ```
782
-
783
- **File:** `src/commands/add.ts`
784
-
785
- ```typescript
786
- // Update journal
787
- if (!options.dryRun) {
788
- await addEntry(paths.journalFile, {
789
- feature: manifest.name,
790
- target: manifest.target,
791
- files: copiedFiles,
792
- insertedNav: navInserted,
793
- navHref,
794
- navLabel,
795
- ts: Date.now(),
796
-
797
- // NEW: Store relevant manifest data
798
- manifest: {
799
- version: manifest.version,
800
- manualSteps: manifest.manualSteps,
801
- env: manifest.env,
802
- postInstall: manifest.postInstall,
803
- },
804
- });
805
- }
806
- ```
807
-
808
-
809
- ---
810
-
811
- ## Phase 4: Modification Detection (Priority 4) 🟡
812
-
813
- ### Goal
814
- Warn users before deleting files they've modified.
815
-
816
- ### Technical Approach
817
-
818
- #### Step 4.1: Hash files during installation
819
-
820
- **File:** `src/core/hash.ts` (NEW)
821
-
822
- ```typescript
823
- import { createHash } from 'crypto';
824
- import { readFile } from 'fs/promises';
825
-
826
- export async function hashFile(filePath: string): Promise<string> {
827
- try {
828
- const content = await readFile(filePath);
829
- return createHash('sha256').update(content).digest('hex');
830
- } catch (error) {
831
- // File doesn't exist or can't be read
832
- return '';
833
- }
834
- }
835
-
836
- export async function hashFiles(
837
- filePaths: string[]
838
- ): Promise<Map<string, string>> {
839
- const hashes = new Map<string, string>();
840
-
841
- await Promise.all(
842
- filePaths.map(async (path) => {
843
- const hash = await hashFile(path);
844
- hashes.set(path, hash);
845
- })
846
- );
847
-
848
- return hashes;
849
- }
850
- ```
851
-
852
- #### Step 4.2: Update journal to store hashes
853
-
854
- **File:** `src/core/journal.ts`
855
-
856
- ```typescript
857
- export interface JournalEntry {
858
- feature: string;
859
- target: 'native' | 'web';
860
-
861
- // CHANGE: files is now an array of objects with hashes
862
- files: Array<{
863
- path: string;
864
- hash: string;
865
- }>;
866
-
867
- insertedNav: boolean;
868
- navHref?: string;
869
- navLabel?: string;
870
- ts: number;
871
- manifest?: any;
872
- }
873
- ```
874
-
875
- **Gotcha:** This is a breaking change! Need migration for existing journals.
876
-
877
- #### Step 4.3: Add migration for old journals
878
-
879
- **File:** `src/core/journal.ts`
880
-
881
- ```typescript
882
- export async function readJournal(path: string): Promise<Journal> {
883
- try {
884
- const content = await readFileContent(path);
885
- const journal = JSON.parse(content) as Journal;
886
-
887
- // Migrate old format to new format
888
- journal.entries = await Promise.all(
889
- journal.entries.map(async (entry) => {
890
- // Check if files is old format (array of strings)
891
- if (entry.files.length > 0 && typeof entry.files[0] === 'string') {
892
- // Migrate to new format
893
- const fileHashes = await hashFiles(entry.files as any);
894
-
895
- entry.files = Array.from(fileHashes.entries()).map(([path, hash]) => ({
896
- path,
897
- hash,
898
- }));
899
- }
900
-
901
- return entry;
902
- })
903
- );
904
-
905
- return journal;
906
- } catch (err: any) {
907
- if (err.code === 'ENOENT') {
908
- return { entries: [] };
909
- }
910
- throw err;
911
- }
912
- }
913
- ```
914
-
915
- #### Step 4.4: Hash files during installation
916
-
917
- **File:** `src/commands/add.ts`
918
-
919
- ```typescript
920
- // After copying files
921
-
922
- // Hash all copied files
923
- log.info('Computing file hashes...');
924
- const fileHashes = await hashFiles(copiedFiles);
925
-
926
- // Update journal with hashes
927
- if (!options.dryRun) {
928
- await addEntry(paths.journalFile, {
929
- feature: manifest.name,
930
- target: manifest.target,
931
- files: Array.from(fileHashes.entries()).map(([path, hash]) => ({
932
- path,
933
- hash,
934
- })),
935
- insertedNav: navInserted,
936
- navHref,
937
- navLabel,
938
- ts: Date.now(),
939
- manifest: {
940
- version: manifest.version,
941
- manualSteps: manifest.manualSteps,
942
- env: manifest.env,
943
- postInstall: manifest.postInstall,
944
- },
945
- });
946
- }
947
- ```
948
-
949
- #### Step 4.5: Check for modifications during removal
950
-
951
- **File:** `src/commands/remove.ts`
952
-
953
- ```typescript
954
- // After getting journal entry
955
-
956
- // Check for modifications
957
- log.info('Checking for file modifications...');
958
-
959
- const modifiedFiles: Array<{ path: string; status: 'modified' | 'deleted' }> = [];
960
-
961
- for (const fileEntry of entry.files) {
962
- const currentHash = await hashFile(fileEntry.path);
963
-
964
- if (currentHash === '') {
965
- // File was deleted by user
966
- modifiedFiles.push({ path: fileEntry.path, status: 'deleted' });
967
- } else if (currentHash !== fileEntry.hash) {
968
- // File was modified by user
969
- modifiedFiles.push({ path: fileEntry.path, status: 'modified' });
970
- }
971
- }
972
-
973
- // Warn about modifications
974
- if (modifiedFiles.length > 0) {
975
- log.plain('');
976
- log.warn('⚠ WARNING: The following files were changed:');
977
- log.plain('');
978
-
979
- const modified = modifiedFiles.filter(f => f.status === 'modified');
980
- const deleted = modifiedFiles.filter(f => f.status === 'deleted');
981
-
982
- if (modified.length > 0) {
983
- log.plain(' Modified by you:');
984
- modified.slice(0, 10).forEach(f => {
985
- const relativePath = f.path.replace(paths.cwd + '/', '');
986
- log.plain(` • ${relativePath}`);
987
- });
988
- if (modified.length > 10) {
989
- log.plain(` ... and ${modified.length - 10} more`);
990
- }
991
- }
992
-
993
- if (deleted.length > 0) {
994
- log.plain('');
995
- log.plain(' Already deleted:');
996
- deleted.slice(0, 5).forEach(f => {
997
- const relativePath = f.path.replace(paths.cwd + '/', '');
998
- log.plain(` • ${relativePath}`);
999
- });
1000
- if (deleted.length > 5) {
1001
- log.plain(` ... and ${deleted.length - 5} more`);
1002
- }
1003
- }
1004
-
1005
- log.plain('');
1006
- log.warn('⚠ Your changes will be LOST if you continue!');
1007
- log.info('💡 Make sure you have committed to Git.');
1008
- log.plain('');
1009
-
1010
- if (!options.force && !options.yes) {
1011
- const shouldContinue = promptYesNo(
1012
- 'Continue with removal? (y/N): ',
1013
- false
1014
- );
1015
-
1016
- if (!shouldContinue) {
1017
- log.info('Removal cancelled');
1018
- return;
1019
- }
1020
- }
1021
- }
1022
-
1023
- // Continue with deletion...
1024
- ```
1025
-
1026
- **Performance Gotcha:** Hashing large files can be slow. Need to show progress.
1027
-
1028
- #### Step 4.6: Add progress indicator
1029
-
1030
- **File:** `src/core/hash.ts`
1031
-
1032
- ```typescript
1033
- export async function hashFiles(
1034
- filePaths: string[],
1035
- options?: { showProgress?: boolean }
1036
- ): Promise<Map<string, string>> {
1037
- const hashes = new Map<string, string>();
1038
-
1039
- if (options?.showProgress && filePaths.length > 10) {
1040
- const { log } = await import('./log.js');
1041
- log.info(`Hashing ${filePaths.length} files...`);
1042
- }
1043
-
1044
- // Process in batches to avoid overwhelming the system
1045
- const batchSize = 10;
1046
- for (let i = 0; i < filePaths.length; i += batchSize) {
1047
- const batch = filePaths.slice(i, i + batchSize);
1048
-
1049
- await Promise.all(
1050
- batch.map(async (path) => {
1051
- const hash = await hashFile(path);
1052
- hashes.set(path, hash);
1053
- })
1054
- );
1055
-
1056
- if (options?.showProgress && filePaths.length > 10) {
1057
- const progress = Math.min(i + batchSize, filePaths.length);
1058
- process.stdout.write(`\r Progress: ${progress}/${filePaths.length}`);
1059
- }
1060
- }
1061
-
1062
- if (options?.showProgress && filePaths.length > 10) {
1063
- process.stdout.write('\n');
1064
- }
1065
-
1066
- return hashes;
1067
- }
1068
- ```
1069
-
1070
- ---
1071
-
1072
- ## Phase 5: Additional Commands (Priority 5) 🟢
1073
-
1074
- ### vf status command
1075
-
1076
- **File:** `src/commands/status.ts` (NEW)
1077
-
1078
- ```typescript
1079
- import { Command } from 'commander';
1080
- import { log } from '../core/log.js';
1081
- import { getPaths } from '../core/paths.js';
1082
- import { readJournal } from '../core/journal.js';
1083
-
1084
- export const statusCommand = new Command('status')
1085
- .description('Show installed features and their status')
1086
- .action(async () => {
1087
- try {
1088
- const paths = getPaths();
1089
- const journal = await readJournal(paths.journalFile);
1090
-
1091
- if (journal.entries.length === 0) {
1092
- log.info('No features installed yet');
1093
- log.info('Run "vf list" to see available features');
1094
- return;
1095
- }
1096
-
1097
- log.info('Installed features:');
1098
- log.plain('');
1099
-
1100
- for (const entry of journal.entries) {
1101
- const version = entry.manifest?.version || 'unknown';
1102
- log.plain(` ✓ ${entry.feature} (v${version}) - ${entry.target}`);
1103
- log.plain(` Files: ${entry.files.length}`);
1104
- log.plain(` Installed: ${new Date(entry.ts).toLocaleDateString()}`);
1105
-
1106
- if (entry.manifest?.manualSteps) {
1107
- log.plain(` ⚠ Has manual setup steps (run: vf checklist ${entry.feature})`);
1108
- }
1109
-
1110
- log.plain('');
1111
- }
1112
-
1113
- } catch (error: any) {
1114
- log.error(`Failed to show status: ${error.message}`);
1115
- process.exit(1);
1116
- }
1117
- });
1118
- ```
1119
-
1120
- ---
1121
-
1122
- ## Testing Strategy
1123
-
1124
- ### Unit Tests
1125
-
1126
- **File:** `src/core/__tests__/hash.test.ts` (NEW)
1127
-
1128
- ```typescript
1129
- import { describe, it, expect } from 'vitest';
1130
- import { hashFile, hashFiles } from '../hash.js';
1131
- import { writeFile, unlink } from 'fs/promises';
1132
- import { join } from 'path';
1133
- import { tmpdir } from 'os';
1134
-
1135
- describe('hash', () => {
1136
- it('should hash file content', async () => {
1137
- const testFile = join(tmpdir(), 'test-hash.txt');
1138
- await writeFile(testFile, 'hello world');
1139
-
1140
- const hash = await hashFile(testFile);
1141
-
1142
- // SHA-256 of "hello world"
1143
- expect(hash).toBe('b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9');
1144
-
1145
- await unlink(testFile);
1146
- });
1147
-
1148
- it('should detect file modifications', async () => {
1149
- const testFile = join(tmpdir(), 'test-modify.txt');
1150
- await writeFile(testFile, 'original');
1151
-
1152
- const hash1 = await hashFile(testFile);
1153
-
1154
- await writeFile(testFile, 'modified');
1155
-
1156
- const hash2 = await hashFile(testFile);
1157
-
1158
- expect(hash1).not.toBe(hash2);
1159
-
1160
- await unlink(testFile);
1161
- });
1162
- });
1163
- ```
1164
-
1165
- ### Integration Tests
1166
-
1167
- **File:** `src/__tests__/add-with-deps.test.ts` (NEW)
1168
-
1169
- ```typescript
1170
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
1171
- import { executeBash } from '../core/shell.js';
1172
- import { join } from 'path';
1173
- import { mkdtemp, rm } from 'fs/promises';
1174
- import { tmpdir } from 'os';
1175
-
1176
- describe('vf add with dependencies', () => {
1177
- let testDir: string;
1178
-
1179
- beforeEach(async () => {
1180
- testDir = await mkdtemp(join(tmpdir(), 'vf-test-'));
1181
- // Setup test repo
1182
- });
1183
-
1184
- afterEach(async () => {
1185
- await rm(testDir, { recursive: true, force: true });
1186
- });
1187
-
1188
- it('should show required packages', async () => {
1189
- const result = await executeBash(
1190
- 'vf add charts --dry-run',
1191
- { cwd: testDir }
1192
- );
1193
-
1194
- expect(result.stdout).toContain('react-native-chart-kit');
1195
- expect(result.stdout).toContain('Install with:');
1196
- });
1197
-
1198
- it('should install packages with --yes', async () => {
1199
- const result = await executeBash(
1200
- 'vf add charts --yes',
1201
- { cwd: testDir }
1202
- );
1203
-
1204
- expect(result.stdout).toContain('Installing packages');
1205
- expect(result.stdout).toContain('Packages installed successfully');
1206
- });
1207
- });
1208
- ```
1209
-
1210
- ---
1211
-
1212
- ## Rollout Plan
1213
-
1214
- ### Week 1: Core Improvements
1215
- - Day 1-2: Interactive confirmation (Phase 1)
1216
- - Day 3-4: Package management (Phase 2)
1217
- - Day 5: Testing and bug fixes
1218
-
1219
- ### Week 2: Advanced Features
1220
- - Day 1-2: Manual steps support (Phase 3)
1221
- - Day 3-4: Modification detection (Phase 4)
1222
- - Day 5: Additional commands (Phase 5)
1223
-
1224
- ### Week 3: Polish & Release
1225
- - Day 1-2: Integration testing
1226
- - Day 3: Documentation
1227
- - Day 4: Beta testing with real features
1228
- - Day 5: Production release
1229
-
1230
- ---
1231
-
1232
- ## Potential Issues & Solutions
1233
-
1234
- ### Issue 1: Monorepo Complexity
1235
- **Problem:** Different workspaces might need different packages
1236
- **Solution:** Detect workspace structure, install in correct location
1237
-
1238
- ### Issue 2: Package Installation Failures
1239
- **Problem:** npm install might fail (network, permissions, etc.)
1240
- **Solution:** Catch errors, show manual install command, don't fail the whole operation
1241
-
1242
- ### Issue 3: Large Files Hashing
1243
- **Problem:** Hashing 100+ files can be slow
1244
- **Solution:** Batch processing, progress indicator, skip large binary files
1245
-
1246
- ### Issue 4: Journal Migration
1247
- **Problem:** Existing journals don't have hashes
1248
- **Solution:** Auto-migrate on read, compute hashes for existing files
1249
-
1250
- ### Issue 5: Non-Interactive Environments
1251
- **Problem:** CI/CD doesn't have TTY for prompts
1252
- **Solution:** Check process.stdin.isTTY, default to safe behavior
1253
-
1254
- ### Issue 6: Recipe Versioning
1255
- **Problem:** Recipe format changes over time
1256
- **Solution:** Add schema version to recipe.json, handle migrations
1257
-
1258
- ---
1259
-
1260
- ## Recipe Creation Guidelines
1261
-
1262
- ### For Recipe Creators
1263
-
1264
- When creating a new recipe, you MUST:
1265
-
1266
- 1. **List all dependencies explicitly**
1267
- ```json
1268
- {
1269
- "dependencies": {
1270
- "npm": {
1271
- "package-name": "^1.0.0"
1272
- }
1273
- }
1274
- }
1275
- ```
1276
-
1277
- 2. **Add manual steps for services**
1278
- ```json
1279
- {
1280
- "manualSteps": [
1281
- {
1282
- "step": 1,
1283
- "title": "Get API Key",
1284
- "description": "Sign up and get your API key",
1285
- "link": "https://service.com/signup"
1286
- }
1287
- ]
1288
- }
1289
- ```
1290
-
1291
- 3. **Document environment variables**
1292
- ```json
1293
- {
1294
- "env": {
1295
- "required": [
1296
- {
1297
- "key": "API_KEY",
1298
- "description": "Your API key from service.com",
1299
- "example": "sk_test_123456"
1300
- }
1301
- ]
1302
- }
1303
- }
1304
- ```
1305
-
1306
- 4. **Test the recipe**
1307
- ```bash
1308
- # Create test project
1309
- $ git clone vibefast-starter test-project
1310
- $ cd test-project
1311
-
1312
- # Test installation
1313
- $ vf add your-feature --dry-run
1314
- $ vf add your-feature
1315
-
1316
- # Verify it works
1317
- $ npm run dev
1318
-
1319
- # Test removal
1320
- $ vf remove your-feature
1321
-
1322
- # Verify clean removal
1323
- $ git status
1324
- ```
1325
-
1326
- ---
1327
-
1328
- ## Success Metrics
1329
-
1330
- ### Before Launch
1331
- - [ ] All 5 phases implemented
1332
- - [ ] 90%+ test coverage
1333
- - [ ] Tested with 5+ real features
1334
- - [ ] Documentation complete
1335
- - [ ] No critical bugs
1336
-
1337
- ### After Launch
1338
- - Monitor error rates
1339
- - Track user feedback
1340
- - Measure installation success rate
1341
- - Monitor package installation failures
1342
-
1343
- ---
1344
-
1345
- ## Summary
1346
-
1347
- This is a **2-3 week project** with clear phases:
1348
-
1349
- 1. **Interactive confirmation** - Prevent accidental overwrites
1350
- 2. **Package management** - Auto-detect and install dependencies
1351
- 3. **Manual steps** - Guide users through service setup
1352
- 4. **Modification detection** - Warn before deleting user changes
1353
- 5. **Additional commands** - Better UX with status, checklist, etc.
1354
-
1355
- The hardest parts are:
1356
- - Monorepo package installation (need to detect workspace structure)
1357
- - Journal migration (breaking change to add hashes)
1358
- - Non-interactive environments (CI/CD compatibility)
1359
-
1360
- But all are solvable with the approaches outlined above.