@doswiftly/cli 0.1.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 (453) hide show
  1. package/README.md +357 -0
  2. package/bin/doswiftly.js +2 -0
  3. package/dist/commands/auth-github.d.ts +6 -0
  4. package/dist/commands/auth-github.d.ts.map +1 -0
  5. package/dist/commands/auth-github.js +89 -0
  6. package/dist/commands/auth-github.js.map +1 -0
  7. package/dist/commands/auth-token.d.ts +12 -0
  8. package/dist/commands/auth-token.d.ts.map +1 -0
  9. package/dist/commands/auth-token.js +43 -0
  10. package/dist/commands/auth-token.js.map +1 -0
  11. package/dist/commands/auth.d.ts +22 -0
  12. package/dist/commands/auth.d.ts.map +1 -0
  13. package/dist/commands/auth.js +348 -0
  14. package/dist/commands/auth.js.map +1 -0
  15. package/dist/commands/check.d.ts +5 -0
  16. package/dist/commands/check.d.ts.map +1 -0
  17. package/dist/commands/check.js +234 -0
  18. package/dist/commands/check.js.map +1 -0
  19. package/dist/commands/config.d.ts +3 -0
  20. package/dist/commands/config.d.ts.map +1 -0
  21. package/dist/commands/config.js +104 -0
  22. package/dist/commands/config.js.map +1 -0
  23. package/dist/commands/deploy.d.ts +37 -0
  24. package/dist/commands/deploy.d.ts.map +1 -0
  25. package/dist/commands/deploy.js +580 -0
  26. package/dist/commands/deploy.js.map +1 -0
  27. package/dist/commands/dev.d.ts +8 -0
  28. package/dist/commands/dev.d.ts.map +1 -0
  29. package/dist/commands/dev.js +83 -0
  30. package/dist/commands/dev.js.map +1 -0
  31. package/dist/commands/doctor.d.ts +5 -0
  32. package/dist/commands/doctor.d.ts.map +1 -0
  33. package/dist/commands/doctor.js +363 -0
  34. package/dist/commands/doctor.js.map +1 -0
  35. package/dist/commands/domain.d.ts +13 -0
  36. package/dist/commands/domain.d.ts.map +1 -0
  37. package/dist/commands/domain.js +128 -0
  38. package/dist/commands/domain.js.map +1 -0
  39. package/dist/commands/env.d.ts +25 -0
  40. package/dist/commands/env.d.ts.map +1 -0
  41. package/dist/commands/env.js +228 -0
  42. package/dist/commands/env.js.map +1 -0
  43. package/dist/commands/init.d.ts +11 -0
  44. package/dist/commands/init.d.ts.map +1 -0
  45. package/dist/commands/init.js +1028 -0
  46. package/dist/commands/init.js.map +1 -0
  47. package/dist/commands/inspect.d.ts +12 -0
  48. package/dist/commands/inspect.d.ts.map +1 -0
  49. package/dist/commands/inspect.js +162 -0
  50. package/dist/commands/inspect.js.map +1 -0
  51. package/dist/commands/migrate.d.ts +18 -0
  52. package/dist/commands/migrate.d.ts.map +1 -0
  53. package/dist/commands/migrate.js +355 -0
  54. package/dist/commands/migrate.js.map +1 -0
  55. package/dist/commands/preview.d.ts +29 -0
  56. package/dist/commands/preview.d.ts.map +1 -0
  57. package/dist/commands/preview.js +199 -0
  58. package/dist/commands/preview.js.map +1 -0
  59. package/dist/commands/proxy.d.ts +9 -0
  60. package/dist/commands/proxy.d.ts.map +1 -0
  61. package/dist/commands/proxy.js +37 -0
  62. package/dist/commands/proxy.js.map +1 -0
  63. package/dist/commands/sdk.d.ts +5 -0
  64. package/dist/commands/sdk.d.ts.map +1 -0
  65. package/dist/commands/sdk.js +82 -0
  66. package/dist/commands/sdk.js.map +1 -0
  67. package/dist/commands/template.d.ts +107 -0
  68. package/dist/commands/template.d.ts.map +1 -0
  69. package/dist/commands/template.js +1309 -0
  70. package/dist/commands/template.js.map +1 -0
  71. package/dist/commands/types.d.ts +5 -0
  72. package/dist/commands/types.d.ts.map +1 -0
  73. package/dist/commands/types.js +82 -0
  74. package/dist/commands/types.js.map +1 -0
  75. package/dist/commands/update.d.ts +2 -0
  76. package/dist/commands/update.d.ts.map +1 -0
  77. package/dist/commands/update.js +103 -0
  78. package/dist/commands/update.js.map +1 -0
  79. package/dist/commands/upgrade.d.ts +18 -0
  80. package/dist/commands/upgrade.d.ts.map +1 -0
  81. package/dist/commands/upgrade.js +55 -0
  82. package/dist/commands/upgrade.js.map +1 -0
  83. package/dist/commands/verify.d.ts +5 -0
  84. package/dist/commands/verify.d.ts.map +1 -0
  85. package/dist/commands/verify.js +232 -0
  86. package/dist/commands/verify.js.map +1 -0
  87. package/dist/commands/whoami.d.ts +5 -0
  88. package/dist/commands/whoami.d.ts.map +1 -0
  89. package/dist/commands/whoami.js +60 -0
  90. package/dist/commands/whoami.js.map +1 -0
  91. package/dist/config/types.d.ts +173 -0
  92. package/dist/config/types.d.ts.map +1 -0
  93. package/dist/config/types.js +48 -0
  94. package/dist/config/types.js.map +1 -0
  95. package/dist/index.d.ts +3 -0
  96. package/dist/index.d.ts.map +1 -0
  97. package/dist/index.js +416 -0
  98. package/dist/index.js.map +1 -0
  99. package/dist/lib/api-url.d.ts +14 -0
  100. package/dist/lib/api-url.d.ts.map +1 -0
  101. package/dist/lib/api-url.js +24 -0
  102. package/dist/lib/api-url.js.map +1 -0
  103. package/dist/lib/api.d.ts +67 -0
  104. package/dist/lib/api.d.ts.map +1 -0
  105. package/dist/lib/api.js +36 -0
  106. package/dist/lib/api.js.map +1 -0
  107. package/dist/lib/config.d.ts +39 -0
  108. package/dist/lib/config.d.ts.map +1 -0
  109. package/dist/lib/config.js +195 -0
  110. package/dist/lib/config.js.map +1 -0
  111. package/dist/lib/env-storage.d.ts +140 -0
  112. package/dist/lib/env-storage.d.ts.map +1 -0
  113. package/dist/lib/env-storage.js +464 -0
  114. package/dist/lib/env-storage.js.map +1 -0
  115. package/dist/lib/errors.d.ts +61 -0
  116. package/dist/lib/errors.d.ts.map +1 -0
  117. package/dist/lib/errors.js +204 -0
  118. package/dist/lib/errors.js.map +1 -0
  119. package/dist/lib/i18n.d.ts +99 -0
  120. package/dist/lib/i18n.d.ts.map +1 -0
  121. package/dist/lib/i18n.js +184 -0
  122. package/dist/lib/i18n.js.map +1 -0
  123. package/dist/lib/logger.d.ts +95 -0
  124. package/dist/lib/logger.d.ts.map +1 -0
  125. package/dist/lib/logger.js +168 -0
  126. package/dist/lib/logger.js.map +1 -0
  127. package/dist/lib/package-manager.d.ts +91 -0
  128. package/dist/lib/package-manager.d.ts.map +1 -0
  129. package/dist/lib/package-manager.js +205 -0
  130. package/dist/lib/package-manager.js.map +1 -0
  131. package/dist/lib/proxy-server.d.ts +24 -0
  132. package/dist/lib/proxy-server.d.ts.map +1 -0
  133. package/dist/lib/proxy-server.js +173 -0
  134. package/dist/lib/proxy-server.js.map +1 -0
  135. package/dist/lib/select-with-back.d.ts +34 -0
  136. package/dist/lib/select-with-back.d.ts.map +1 -0
  137. package/dist/lib/select-with-back.js +94 -0
  138. package/dist/lib/select-with-back.js.map +1 -0
  139. package/dist/lib/shared-api-client.d.ts +40 -0
  140. package/dist/lib/shared-api-client.d.ts.map +1 -0
  141. package/dist/lib/shared-api-client.js +92 -0
  142. package/dist/lib/shared-api-client.js.map +1 -0
  143. package/dist/lib/wizard-engine.d.ts +128 -0
  144. package/dist/lib/wizard-engine.d.ts.map +1 -0
  145. package/dist/lib/wizard-engine.js +168 -0
  146. package/dist/lib/wizard-engine.js.map +1 -0
  147. package/package.json +85 -0
  148. package/templates/storefront-minimal/.env.example +10 -0
  149. package/templates/storefront-minimal/.github/workflows/build-template.yml +109 -0
  150. package/templates/storefront-minimal/app/globals.css +18 -0
  151. package/templates/storefront-minimal/app/layout.tsx +26 -0
  152. package/templates/storefront-minimal/app/page.tsx +93 -0
  153. package/templates/storefront-minimal/lib/graphql-client.ts +23 -0
  154. package/templates/storefront-minimal/next.config.ts +15 -0
  155. package/templates/storefront-minimal/open-next.config.ts +3 -0
  156. package/templates/storefront-minimal/package.json +30 -0
  157. package/templates/storefront-minimal/postcss.config.mjs +5 -0
  158. package/templates/storefront-minimal/tailwind.config.ts +14 -0
  159. package/templates/storefront-minimal/tsconfig.json +27 -0
  160. package/templates/storefront-minimal/wrangler.toml +9 -0
  161. package/templates/storefront-nextjs/.env.example +68 -0
  162. package/templates/storefront-nextjs/.github/workflows/build-template.yml +109 -0
  163. package/templates/storefront-nextjs/.github/workflows/deploy.yml +25 -0
  164. package/templates/storefront-nextjs/.github/workflows/preview.yml +22 -0
  165. package/templates/storefront-nextjs/README.md +520 -0
  166. package/templates/storefront-nextjs/app/account/orders/page.tsx +216 -0
  167. package/templates/storefront-nextjs/app/account/page.tsx +167 -0
  168. package/templates/storefront-nextjs/app/auth/login/page.tsx +135 -0
  169. package/templates/storefront-nextjs/app/auth/register/page.tsx +228 -0
  170. package/templates/storefront-nextjs/app/cart/page.tsx +263 -0
  171. package/templates/storefront-nextjs/app/categories/[slug]/page.tsx +200 -0
  172. package/templates/storefront-nextjs/app/categories/page.tsx +58 -0
  173. package/templates/storefront-nextjs/app/checkout/page.tsx +351 -0
  174. package/templates/storefront-nextjs/app/collections/[slug]/page.tsx +158 -0
  175. package/templates/storefront-nextjs/app/collections/page.tsx +61 -0
  176. package/templates/storefront-nextjs/app/globals.css +98 -0
  177. package/templates/storefront-nextjs/app/layout.tsx +39 -0
  178. package/templates/storefront-nextjs/app/page.tsx +136 -0
  179. package/templates/storefront-nextjs/app/products/[slug]/page.tsx +119 -0
  180. package/templates/storefront-nextjs/app/products/page.tsx +107 -0
  181. package/templates/storefront-nextjs/app/search/page.tsx +127 -0
  182. package/templates/storefront-nextjs/components/auth/auth-guard.tsx +94 -0
  183. package/templates/storefront-nextjs/components/commerce/add-to-cart-button.tsx +77 -0
  184. package/templates/storefront-nextjs/components/commerce/cart-icon.tsx +29 -0
  185. package/templates/storefront-nextjs/components/commerce/currency-selector.tsx +217 -0
  186. package/templates/storefront-nextjs/components/commerce/pagination.tsx +62 -0
  187. package/templates/storefront-nextjs/components/commerce/product-actions.tsx +135 -0
  188. package/templates/storefront-nextjs/components/commerce/product-filters.tsx +109 -0
  189. package/templates/storefront-nextjs/components/commerce/product-price.tsx +375 -0
  190. package/templates/storefront-nextjs/components/commerce/search-input.tsx +178 -0
  191. package/templates/storefront-nextjs/components/commerce/sort-select.tsx +64 -0
  192. package/templates/storefront-nextjs/components/commerce/variant-selector.tsx +210 -0
  193. package/templates/storefront-nextjs/components/layout/footer.tsx +107 -0
  194. package/templates/storefront-nextjs/components/layout/header.tsx +104 -0
  195. package/templates/storefront-nextjs/components/providers.tsx +62 -0
  196. package/templates/storefront-nextjs/lib/auth/routes.ts +52 -0
  197. package/templates/storefront-nextjs/lib/currency.tsx +140 -0
  198. package/templates/storefront-nextjs/lib/format.ts +159 -0
  199. package/templates/storefront-nextjs/lib/graphql-queries.ts +629 -0
  200. package/templates/storefront-nextjs/lib/hooks.ts +30 -0
  201. package/templates/storefront-nextjs/middleware.ts +80 -0
  202. package/templates/storefront-nextjs/next.config.ts +37 -0
  203. package/templates/storefront-nextjs/open-next.config.ts +3 -0
  204. package/templates/storefront-nextjs/package.dev.json +30 -0
  205. package/templates/storefront-nextjs/package.json +32 -0
  206. package/templates/storefront-nextjs/package.json.template +32 -0
  207. package/templates/storefront-nextjs/postcss.config.mjs +8 -0
  208. package/templates/storefront-nextjs/tailwind.config.ts +111 -0
  209. package/templates/storefront-nextjs/tsconfig.json +27 -0
  210. package/templates/storefront-nextjs/wrangler.toml +9 -0
  211. package/templates/storefront-nextjs-shadcn/.env.example +68 -0
  212. package/templates/storefront-nextjs-shadcn/.github/workflows/build-template.yml +109 -0
  213. package/templates/storefront-nextjs-shadcn/.github/workflows/deploy.yml +25 -0
  214. package/templates/storefront-nextjs-shadcn/.github/workflows/preview.yml +22 -0
  215. package/templates/storefront-nextjs-shadcn/CART_INTEGRATION.md +282 -0
  216. package/templates/storefront-nextjs-shadcn/CLAUDE.md +96 -0
  217. package/templates/storefront-nextjs-shadcn/GRAPHQL_DOCUMENT_NAMES.md +190 -0
  218. package/templates/storefront-nextjs-shadcn/GRAPHQL_ERROR_HANDLING.md +263 -0
  219. package/templates/storefront-nextjs-shadcn/GRAPHQL_FIXES_SUMMARY.md +135 -0
  220. package/templates/storefront-nextjs-shadcn/GRAPHQL_INTEGRATION_COMPLETE.md +142 -0
  221. package/templates/storefront-nextjs-shadcn/INTEGRATION_CHECKLIST.md +448 -0
  222. package/templates/storefront-nextjs-shadcn/PRODUCT_DETAIL_PAGE_IMPLEMENTATION.md +307 -0
  223. package/templates/storefront-nextjs-shadcn/README.md +195 -0
  224. package/templates/storefront-nextjs-shadcn/THEME_CUSTOMIZATION.md +245 -0
  225. package/templates/storefront-nextjs-shadcn/app/about/page.tsx +34 -0
  226. package/templates/storefront-nextjs-shadcn/app/account/addresses/page.tsx +215 -0
  227. package/templates/storefront-nextjs-shadcn/app/account/loyalty/page.tsx +484 -0
  228. package/templates/storefront-nextjs-shadcn/app/account/orders/[id]/page.tsx +128 -0
  229. package/templates/storefront-nextjs-shadcn/app/account/orders/[id]/tracking/page.tsx +206 -0
  230. package/templates/storefront-nextjs-shadcn/app/account/orders/page.tsx +80 -0
  231. package/templates/storefront-nextjs-shadcn/app/account/page.tsx +107 -0
  232. package/templates/storefront-nextjs-shadcn/app/account/settings/page.tsx +195 -0
  233. package/templates/storefront-nextjs-shadcn/app/api/auth/clear-token/route.ts +87 -0
  234. package/templates/storefront-nextjs-shadcn/app/api/auth/set-token/route.ts +125 -0
  235. package/templates/storefront-nextjs-shadcn/app/auth/forgot-password/page.tsx +131 -0
  236. package/templates/storefront-nextjs-shadcn/app/auth/login/page.tsx +24 -0
  237. package/templates/storefront-nextjs-shadcn/app/auth/register/page.tsx +20 -0
  238. package/templates/storefront-nextjs-shadcn/app/blog/[slug]/page.tsx +323 -0
  239. package/templates/storefront-nextjs-shadcn/app/blog/page.tsx +159 -0
  240. package/templates/storefront-nextjs-shadcn/app/brands/[slug]/page.tsx +170 -0
  241. package/templates/storefront-nextjs-shadcn/app/brands/page.tsx +73 -0
  242. package/templates/storefront-nextjs-shadcn/app/cart/page.tsx +165 -0
  243. package/templates/storefront-nextjs-shadcn/app/categories/[slug]/page.tsx +78 -0
  244. package/templates/storefront-nextjs-shadcn/app/categories/page.tsx +75 -0
  245. package/templates/storefront-nextjs-shadcn/app/checkout/page.tsx +1752 -0
  246. package/templates/storefront-nextjs-shadcn/app/checkout/success/[orderId]/page.tsx +256 -0
  247. package/templates/storefront-nextjs-shadcn/app/collections/[handle]/page.tsx +74 -0
  248. package/templates/storefront-nextjs-shadcn/app/collections/page.tsx +75 -0
  249. package/templates/storefront-nextjs-shadcn/app/contact/page.tsx +114 -0
  250. package/templates/storefront-nextjs-shadcn/app/error.tsx +90 -0
  251. package/templates/storefront-nextjs-shadcn/app/globals.css +125 -0
  252. package/templates/storefront-nextjs-shadcn/app/layout.tsx +57 -0
  253. package/templates/storefront-nextjs-shadcn/app/not-found.tsx +68 -0
  254. package/templates/storefront-nextjs-shadcn/app/page.tsx +21 -0
  255. package/templates/storefront-nextjs-shadcn/app/products/[slug]/page.tsx +246 -0
  256. package/templates/storefront-nextjs-shadcn/app/products/[slug]/product-client.tsx +343 -0
  257. package/templates/storefront-nextjs-shadcn/app/products/page.tsx +25 -0
  258. package/templates/storefront-nextjs-shadcn/app/products/products-client.tsx +192 -0
  259. package/templates/storefront-nextjs-shadcn/app/returns/page.tsx +77 -0
  260. package/templates/storefront-nextjs-shadcn/app/robots.ts +53 -0
  261. package/templates/storefront-nextjs-shadcn/app/search/page.tsx +16 -0
  262. package/templates/storefront-nextjs-shadcn/app/search/search-client.tsx +47 -0
  263. package/templates/storefront-nextjs-shadcn/app/shipping/page.tsx +62 -0
  264. package/templates/storefront-nextjs-shadcn/app/sitemap.ts +144 -0
  265. package/templates/storefront-nextjs-shadcn/app/wishlist/page.tsx +179 -0
  266. package/templates/storefront-nextjs-shadcn/codegen.ts +51 -0
  267. package/templates/storefront-nextjs-shadcn/components/account/address-form.tsx +348 -0
  268. package/templates/storefront-nextjs-shadcn/components/account/address-list.tsx +144 -0
  269. package/templates/storefront-nextjs-shadcn/components/account/order-details.tsx +258 -0
  270. package/templates/storefront-nextjs-shadcn/components/account/order-history.tsx +107 -0
  271. package/templates/storefront-nextjs-shadcn/components/auth/account-menu.tsx +132 -0
  272. package/templates/storefront-nextjs-shadcn/components/auth/login-form.tsx +188 -0
  273. package/templates/storefront-nextjs-shadcn/components/auth/register-form.tsx +305 -0
  274. package/templates/storefront-nextjs-shadcn/components/blog/blog-card.tsx +240 -0
  275. package/templates/storefront-nextjs-shadcn/components/blog/blog-sidebar.tsx +177 -0
  276. package/templates/storefront-nextjs-shadcn/components/blog/index.ts +8 -0
  277. package/templates/storefront-nextjs-shadcn/components/brand/brand-card.tsx +119 -0
  278. package/templates/storefront-nextjs-shadcn/components/brand/brand-grid.tsx +64 -0
  279. package/templates/storefront-nextjs-shadcn/components/cart/cart-drawer.tsx +140 -0
  280. package/templates/storefront-nextjs-shadcn/components/cart/cart-icon.tsx +48 -0
  281. package/templates/storefront-nextjs-shadcn/components/cart/cart-item.tsx +112 -0
  282. package/templates/storefront-nextjs-shadcn/components/cart/cart-summary.tsx +84 -0
  283. package/templates/storefront-nextjs-shadcn/components/cart/index.ts +17 -0
  284. package/templates/storefront-nextjs-shadcn/components/cart/promo-code-input.tsx +121 -0
  285. package/templates/storefront-nextjs-shadcn/components/cart/shipping-estimator.tsx +162 -0
  286. package/templates/storefront-nextjs-shadcn/components/checkout/index.ts +25 -0
  287. package/templates/storefront-nextjs-shadcn/components/checkout/payment-method-card.tsx +187 -0
  288. package/templates/storefront-nextjs-shadcn/components/checkout/payment-step.tsx +160 -0
  289. package/templates/storefront-nextjs-shadcn/components/checkout/tax-breakdown.tsx +154 -0
  290. package/templates/storefront-nextjs-shadcn/components/commerce/currency-selector.tsx +225 -0
  291. package/templates/storefront-nextjs-shadcn/components/commerce/pagination.tsx +62 -0
  292. package/templates/storefront-nextjs-shadcn/components/commerce/product-actions.tsx +158 -0
  293. package/templates/storefront-nextjs-shadcn/components/commerce/search-input.tsx +174 -0
  294. package/templates/storefront-nextjs-shadcn/components/commerce/variant-selector.tsx +210 -0
  295. package/templates/storefront-nextjs-shadcn/components/common/category-card.tsx +97 -0
  296. package/templates/storefront-nextjs-shadcn/components/common/collection-card.tsx +187 -0
  297. package/templates/storefront-nextjs-shadcn/components/common/price-display.tsx +151 -0
  298. package/templates/storefront-nextjs-shadcn/components/common/social-share.tsx +166 -0
  299. package/templates/storefront-nextjs-shadcn/components/discount/discount-breakdown.tsx +245 -0
  300. package/templates/storefront-nextjs-shadcn/components/discount/discount-code-input.tsx +246 -0
  301. package/templates/storefront-nextjs-shadcn/components/discount/index.ts +19 -0
  302. package/templates/storefront-nextjs-shadcn/components/error/error-boundary.tsx +113 -0
  303. package/templates/storefront-nextjs-shadcn/components/error/index.ts +7 -0
  304. package/templates/storefront-nextjs-shadcn/components/filters/attribute-filter.tsx +153 -0
  305. package/templates/storefront-nextjs-shadcn/components/filters/checkbox-group-filter.tsx +167 -0
  306. package/templates/storefront-nextjs-shadcn/components/filters/color-swatch-filter.tsx +176 -0
  307. package/templates/storefront-nextjs-shadcn/components/filters/dynamic-attribute-filters.tsx +220 -0
  308. package/templates/storefront-nextjs-shadcn/components/filters/index.ts +36 -0
  309. package/templates/storefront-nextjs-shadcn/components/filters/range-slider-filter.tsx +193 -0
  310. package/templates/storefront-nextjs-shadcn/components/filters/toggle-filter.tsx +132 -0
  311. package/templates/storefront-nextjs-shadcn/components/gift-card/gift-card-balance.tsx +321 -0
  312. package/templates/storefront-nextjs-shadcn/components/gift-card/gift-card-input.tsx +309 -0
  313. package/templates/storefront-nextjs-shadcn/components/gift-card/index.ts +24 -0
  314. package/templates/storefront-nextjs-shadcn/components/home/category-grid.tsx +72 -0
  315. package/templates/storefront-nextjs-shadcn/components/home/featured-collections.tsx +107 -0
  316. package/templates/storefront-nextjs-shadcn/components/home/featured-products.tsx +85 -0
  317. package/templates/storefront-nextjs-shadcn/components/home/hero-section.tsx +34 -0
  318. package/templates/storefront-nextjs-shadcn/components/home/index.ts +8 -0
  319. package/templates/storefront-nextjs-shadcn/components/home/newsletter-signup.tsx +108 -0
  320. package/templates/storefront-nextjs-shadcn/components/layout/breadcrumbs.tsx +133 -0
  321. package/templates/storefront-nextjs-shadcn/components/layout/currency-selector.tsx +341 -0
  322. package/templates/storefront-nextjs-shadcn/components/layout/footer.tsx +128 -0
  323. package/templates/storefront-nextjs-shadcn/components/layout/header.tsx +147 -0
  324. package/templates/storefront-nextjs-shadcn/components/layout/index.ts +9 -0
  325. package/templates/storefront-nextjs-shadcn/components/layout/mobile-menu.tsx +211 -0
  326. package/templates/storefront-nextjs-shadcn/components/layout/navigation.tsx +95 -0
  327. package/templates/storefront-nextjs-shadcn/components/layout/theme-switcher.tsx +192 -0
  328. package/templates/storefront-nextjs-shadcn/components/loyalty/index.ts +11 -0
  329. package/templates/storefront-nextjs-shadcn/components/loyalty/points-balance.tsx +93 -0
  330. package/templates/storefront-nextjs-shadcn/components/loyalty/points-history.tsx +177 -0
  331. package/templates/storefront-nextjs-shadcn/components/loyalty/referral-section.tsx +250 -0
  332. package/templates/storefront-nextjs-shadcn/components/loyalty/rewards-catalog.tsx +217 -0
  333. package/templates/storefront-nextjs-shadcn/components/loyalty/tier-badge.tsx +106 -0
  334. package/templates/storefront-nextjs-shadcn/components/loyalty/tier-progress.tsx +131 -0
  335. package/templates/storefront-nextjs-shadcn/components/order/delivery-estimate.tsx +196 -0
  336. package/templates/storefront-nextjs-shadcn/components/order/index.ts +11 -0
  337. package/templates/storefront-nextjs-shadcn/components/order/order-tracking.tsx +200 -0
  338. package/templates/storefront-nextjs-shadcn/components/order/shipment-card.tsx +407 -0
  339. package/templates/storefront-nextjs-shadcn/components/order/tracking-status.tsx +222 -0
  340. package/templates/storefront-nextjs-shadcn/components/order/tracking-timeline.tsx +205 -0
  341. package/templates/storefront-nextjs-shadcn/components/product/add-to-cart-button.tsx +161 -0
  342. package/templates/storefront-nextjs-shadcn/components/product/b2b-price-display.tsx +250 -0
  343. package/templates/storefront-nextjs-shadcn/components/product/discount-badge.tsx +196 -0
  344. package/templates/storefront-nextjs-shadcn/components/product/index.ts +41 -0
  345. package/templates/storefront-nextjs-shadcn/components/product/product-card.tsx +147 -0
  346. package/templates/storefront-nextjs-shadcn/components/product/product-filters.tsx +217 -0
  347. package/templates/storefront-nextjs-shadcn/components/product/product-gallery.tsx +143 -0
  348. package/templates/storefront-nextjs-shadcn/components/product/product-grid.tsx +83 -0
  349. package/templates/storefront-nextjs-shadcn/components/product/product-image.tsx +155 -0
  350. package/templates/storefront-nextjs-shadcn/components/product/product-price.tsx +158 -0
  351. package/templates/storefront-nextjs-shadcn/components/product/product-quantity-selector.tsx +111 -0
  352. package/templates/storefront-nextjs-shadcn/components/product/product-reviews.tsx +238 -0
  353. package/templates/storefront-nextjs-shadcn/components/product/product-sort.tsx +58 -0
  354. package/templates/storefront-nextjs-shadcn/components/product/product-variant-selector.tsx +169 -0
  355. package/templates/storefront-nextjs-shadcn/components/product/review-card.tsx +220 -0
  356. package/templates/storefront-nextjs-shadcn/components/product/review-form.tsx +338 -0
  357. package/templates/storefront-nextjs-shadcn/components/product/review-summary.tsx +143 -0
  358. package/templates/storefront-nextjs-shadcn/components/product/sale-countdown.tsx +166 -0
  359. package/templates/storefront-nextjs-shadcn/components/product/savings-display.tsx +213 -0
  360. package/templates/storefront-nextjs-shadcn/components/product/similar-products.tsx +57 -0
  361. package/templates/storefront-nextjs-shadcn/components/product/stock-indicator.tsx +91 -0
  362. package/templates/storefront-nextjs-shadcn/components/providers/currency-provider.tsx +103 -0
  363. package/templates/storefront-nextjs-shadcn/components/providers/index.ts +8 -0
  364. package/templates/storefront-nextjs-shadcn/components/providers/query-provider.tsx +260 -0
  365. package/templates/storefront-nextjs-shadcn/components/providers/theme-provider.tsx +13 -0
  366. package/templates/storefront-nextjs-shadcn/components/returns/index.ts +26 -0
  367. package/templates/storefront-nextjs-shadcn/components/returns/return-request-form.tsx +608 -0
  368. package/templates/storefront-nextjs-shadcn/components/returns/return-status-card.tsx +554 -0
  369. package/templates/storefront-nextjs-shadcn/components/search/index.ts +8 -0
  370. package/templates/storefront-nextjs-shadcn/components/search/search-bar.tsx +140 -0
  371. package/templates/storefront-nextjs-shadcn/components/search/search-results.tsx +58 -0
  372. package/templates/storefront-nextjs-shadcn/components/search/search-suggestions.tsx +43 -0
  373. package/templates/storefront-nextjs-shadcn/components/seo/index.ts +12 -0
  374. package/templates/storefront-nextjs-shadcn/components/seo/json-ld.tsx +56 -0
  375. package/templates/storefront-nextjs-shadcn/components/seo/product-json-ld.ts +167 -0
  376. package/templates/storefront-nextjs-shadcn/components/shipping/index.ts +16 -0
  377. package/templates/storefront-nextjs-shadcn/components/shipping/shipping-method-selector.tsx +337 -0
  378. package/templates/storefront-nextjs-shadcn/components/ui/accordion.tsx +153 -0
  379. package/templates/storefront-nextjs-shadcn/components/ui/alert.tsx +59 -0
  380. package/templates/storefront-nextjs-shadcn/components/ui/badge.tsx +34 -0
  381. package/templates/storefront-nextjs-shadcn/components/ui/button.tsx +51 -0
  382. package/templates/storefront-nextjs-shadcn/components/ui/card.tsx +77 -0
  383. package/templates/storefront-nextjs-shadcn/components/ui/checkbox.tsx +30 -0
  384. package/templates/storefront-nextjs-shadcn/components/ui/dialog.tsx +137 -0
  385. package/templates/storefront-nextjs-shadcn/components/ui/empty-state.tsx +207 -0
  386. package/templates/storefront-nextjs-shadcn/components/ui/index.ts +67 -0
  387. package/templates/storefront-nextjs-shadcn/components/ui/input.tsx +65 -0
  388. package/templates/storefront-nextjs-shadcn/components/ui/label.tsx +26 -0
  389. package/templates/storefront-nextjs-shadcn/components/ui/pagination.tsx +205 -0
  390. package/templates/storefront-nextjs-shadcn/components/ui/radio-group.tsx +44 -0
  391. package/templates/storefront-nextjs-shadcn/components/ui/select.tsx +160 -0
  392. package/templates/storefront-nextjs-shadcn/components/ui/separator.tsx +28 -0
  393. package/templates/storefront-nextjs-shadcn/components/ui/skeleton.tsx +20 -0
  394. package/templates/storefront-nextjs-shadcn/components/ui/spinner.tsx +82 -0
  395. package/templates/storefront-nextjs-shadcn/components/ui/tabs.tsx +119 -0
  396. package/templates/storefront-nextjs-shadcn/components/ui/toast.tsx +96 -0
  397. package/templates/storefront-nextjs-shadcn/components/wishlist/index.ts +9 -0
  398. package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-button.tsx +148 -0
  399. package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-icon.tsx +47 -0
  400. package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-item.tsx +165 -0
  401. package/templates/storefront-nextjs-shadcn/components.json +19 -0
  402. package/templates/storefront-nextjs-shadcn/generated/.gitkeep +2 -0
  403. package/templates/storefront-nextjs-shadcn/graphql/.gitkeep +31 -0
  404. package/templates/storefront-nextjs-shadcn/graphql/collections.example.ts +168 -0
  405. package/templates/storefront-nextjs-shadcn/graphql/products.example.ts +160 -0
  406. package/templates/storefront-nextjs-shadcn/hooks/index.ts +9 -0
  407. package/templates/storefront-nextjs-shadcn/hooks/use-auth.ts +310 -0
  408. package/templates/storefront-nextjs-shadcn/hooks/use-cart-actions.ts +286 -0
  409. package/templates/storefront-nextjs-shadcn/hooks/use-cart-sync.ts +110 -0
  410. package/templates/storefront-nextjs-shadcn/hooks/use-filter-params.test.ts +173 -0
  411. package/templates/storefront-nextjs-shadcn/hooks/use-filter-params.ts +298 -0
  412. package/templates/storefront-nextjs-shadcn/lib/auth/cookies.ts +220 -0
  413. package/templates/storefront-nextjs-shadcn/lib/auth/routes.ts +57 -0
  414. package/templates/storefront-nextjs-shadcn/lib/config.ts +46 -0
  415. package/templates/storefront-nextjs-shadcn/lib/currency/IMPLEMENTATION_SUMMARY.md +254 -0
  416. package/templates/storefront-nextjs-shadcn/lib/currency/README.md +464 -0
  417. package/templates/storefront-nextjs-shadcn/lib/currency/cookie-manager.test.ts +328 -0
  418. package/templates/storefront-nextjs-shadcn/lib/currency/cookie-manager.ts +295 -0
  419. package/templates/storefront-nextjs-shadcn/lib/currency/index.ts +27 -0
  420. package/templates/storefront-nextjs-shadcn/lib/format.test.ts +397 -0
  421. package/templates/storefront-nextjs-shadcn/lib/format.ts +226 -0
  422. package/templates/storefront-nextjs-shadcn/lib/graphql/client.ts +109 -0
  423. package/templates/storefront-nextjs-shadcn/lib/graphql/hooks.ts +1183 -0
  424. package/templates/storefront-nextjs-shadcn/lib/graphql/server.ts +267 -0
  425. package/templates/storefront-nextjs-shadcn/lib/hooks.ts +30 -0
  426. package/templates/storefront-nextjs-shadcn/lib/theme/theme-config.ts +89 -0
  427. package/templates/storefront-nextjs-shadcn/lib/utils.ts +6 -0
  428. package/templates/storefront-nextjs-shadcn/next.config.ts +47 -0
  429. package/templates/storefront-nextjs-shadcn/open-next.config.ts +3 -0
  430. package/templates/storefront-nextjs-shadcn/package.dev.json +30 -0
  431. package/templates/storefront-nextjs-shadcn/package.json +60 -0
  432. package/templates/storefront-nextjs-shadcn/package.json.template +46 -0
  433. package/templates/storefront-nextjs-shadcn/postcss.config.mjs +8 -0
  434. package/templates/storefront-nextjs-shadcn/proxy.ts +80 -0
  435. package/templates/storefront-nextjs-shadcn/public/icons/payment/apple-pay.svg +8 -0
  436. package/templates/storefront-nextjs-shadcn/public/icons/payment/bank-transfer.svg +10 -0
  437. package/templates/storefront-nextjs-shadcn/public/icons/payment/blik.svg +6 -0
  438. package/templates/storefront-nextjs-shadcn/public/icons/payment/cash-on-delivery.svg +11 -0
  439. package/templates/storefront-nextjs-shadcn/public/icons/payment/google-pay.svg +11 -0
  440. package/templates/storefront-nextjs-shadcn/public/icons/payment/mastercard.svg +7 -0
  441. package/templates/storefront-nextjs-shadcn/public/icons/payment/paypal.svg +7 -0
  442. package/templates/storefront-nextjs-shadcn/public/icons/payment/payu.svg +7 -0
  443. package/templates/storefront-nextjs-shadcn/public/icons/payment/przelewy24.svg +7 -0
  444. package/templates/storefront-nextjs-shadcn/public/icons/payment/stripe.svg +4 -0
  445. package/templates/storefront-nextjs-shadcn/public/icons/payment/visa.svg +5 -0
  446. package/templates/storefront-nextjs-shadcn/stores/auth-store.ts +66 -0
  447. package/templates/storefront-nextjs-shadcn/stores/cart-store.ts +56 -0
  448. package/templates/storefront-nextjs-shadcn/stores/checkout-store.ts +184 -0
  449. package/templates/storefront-nextjs-shadcn/stores/currency-store.ts +103 -0
  450. package/templates/storefront-nextjs-shadcn/stores/wishlist-store.ts +291 -0
  451. package/templates/storefront-nextjs-shadcn/tailwind.config.ts +111 -0
  452. package/templates/storefront-nextjs-shadcn/tsconfig.json +27 -0
  453. package/templates/storefront-nextjs-shadcn/wrangler.toml +9 -0
@@ -0,0 +1,1028 @@
1
+ import * as p from "@clack/prompts";
2
+ import pc from "picocolors";
3
+ import ora from "ora";
4
+ import { existsSync, writeFileSync, mkdirSync, readFileSync, readdirSync, statSync, } from "fs";
5
+ import { join, dirname, basename } from "path";
6
+ import { fileURLToPath } from "url";
7
+ import { execSync, execFileSync } from "child_process";
8
+ import { detectPackageManager, getPackageManagerInfo, installDependencies, } from "../lib/package-manager.js";
9
+ import { getStoredToken } from "./auth.js";
10
+ import { hasProfiles, addProfile } from "../lib/env-storage.js";
11
+ import { getApiUrl } from "../lib/api-url.js";
12
+ import { WizardEngine, selectWithBack, isBack, isCancel, next, back, cancel, clearScreen, } from "../lib/wizard-engine.js";
13
+ import { t, initI18n } from "../lib/i18n.js";
14
+ const __filename = fileURLToPath(import.meta.url);
15
+ const __dirname = dirname(__filename);
16
+ // Initialize i18n
17
+ initI18n();
18
+ // ============================================================================
19
+ // API Functions
20
+ // ============================================================================
21
+ /**
22
+ * Fetch user's teams from CLI API
23
+ */
24
+ async function fetchTeams(apiUrl, token) {
25
+ try {
26
+ const response = await fetch(`${apiUrl}/cli/teams`, {
27
+ headers: {
28
+ Authorization: `Bearer ${token}`,
29
+ },
30
+ });
31
+ if (!response.ok) {
32
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
33
+ }
34
+ const data = (await response.json());
35
+ return data.teams || [];
36
+ }
37
+ catch (error) {
38
+ p.log.warn(t().failedFetchTeams(error.message));
39
+ return [];
40
+ }
41
+ }
42
+ /**
43
+ * Fetch user's projects from CLI API
44
+ */
45
+ async function fetchProjects(apiUrl, token) {
46
+ try {
47
+ const response = await fetch(`${apiUrl}/cli/projects`, {
48
+ headers: {
49
+ Authorization: `Bearer ${token}`,
50
+ },
51
+ });
52
+ if (!response.ok) {
53
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
54
+ }
55
+ const data = (await response.json());
56
+ return data.projects || [];
57
+ }
58
+ catch (error) {
59
+ p.log.warn(t().failedFetchProjects(error.message));
60
+ return [];
61
+ }
62
+ }
63
+ /**
64
+ * Fetch templates from the remote registry API.
65
+ */
66
+ async function fetchRemoteTemplates(apiUrl, token) {
67
+ try {
68
+ const response = await fetch(`${apiUrl}/cli/templates`, {
69
+ headers: {
70
+ Authorization: `Bearer ${token}`,
71
+ },
72
+ });
73
+ if (!response.ok) {
74
+ return null;
75
+ }
76
+ const data = (await response.json());
77
+ return data.filter((tpl) => tpl.status === "PUBLISHED");
78
+ }
79
+ catch {
80
+ return null;
81
+ }
82
+ }
83
+ /**
84
+ * Download a remote template using degit.
85
+ */
86
+ async function downloadTemplate(repoUrl, targetDir) {
87
+ // Validate the repo URL to prevent path traversal and restrict to trusted hosts
88
+ const allowedHosts = ['github.com', 'gitlab.com', 'bitbucket.org'];
89
+ try {
90
+ const parsed = new URL(repoUrl.startsWith('http') ? repoUrl : `https://github.com/${repoUrl}`);
91
+ if (!allowedHosts.includes(parsed.hostname)) {
92
+ throw new Error(`Untrusted repository host: ${parsed.hostname}. Allowed: ${allowedHosts.join(', ')}`);
93
+ }
94
+ if (parsed.pathname.includes('..')) {
95
+ throw new Error('Path traversal detected in repository URL');
96
+ }
97
+ }
98
+ catch (e) {
99
+ if (e instanceof Error && (e.message.includes('Untrusted') || e.message.includes('traversal')))
100
+ throw e;
101
+ // degit supports shorthand like "user/repo" — validate format
102
+ if (!/^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+(?:#[a-zA-Z0-9_.-]+)?$/.test(repoUrl)) {
103
+ throw new Error(`Invalid repository URL format: ${repoUrl}`);
104
+ }
105
+ }
106
+ const { default: degit } = await import("degit");
107
+ const emitter = degit(repoUrl, { cache: false, force: true });
108
+ await emitter.clone(targetDir);
109
+ }
110
+ // ============================================================================
111
+ // Template Utilities
112
+ // ============================================================================
113
+ /**
114
+ * Apply placeholder replacements to all files in a directory.
115
+ */
116
+ function applyReplacementsToDirectory(dir, replacements) {
117
+ const textExtensions = new Set([
118
+ ".ts",
119
+ ".tsx",
120
+ ".js",
121
+ ".jsx",
122
+ ".json",
123
+ ".yaml",
124
+ ".yml",
125
+ ".md",
126
+ ".mdx",
127
+ ".html",
128
+ ".css",
129
+ ".env",
130
+ ".env.example",
131
+ ".env.local",
132
+ ".toml",
133
+ ".cfg",
134
+ ]);
135
+ const entries = readdirSync(dir);
136
+ for (const entry of entries) {
137
+ const fullPath = join(dir, entry);
138
+ const stat = statSync(fullPath);
139
+ if (stat.isDirectory()) {
140
+ if (entry === "node_modules" || entry === ".git")
141
+ continue;
142
+ applyReplacementsToDirectory(fullPath, replacements);
143
+ }
144
+ else {
145
+ const ext = entry.includes(".") ? "." + entry.split(".").pop() : "";
146
+ const isTextFile = textExtensions.has(ext) || entry.startsWith(".env");
147
+ if (!isTextFile)
148
+ continue;
149
+ try {
150
+ let content = readFileSync(fullPath, "utf-8");
151
+ let changed = false;
152
+ for (const [placeholder, value] of Object.entries(replacements)) {
153
+ const regex = new RegExp(placeholder, "g");
154
+ const newContent = content.replace(regex, value);
155
+ if (newContent !== content) {
156
+ content = newContent;
157
+ changed = true;
158
+ }
159
+ }
160
+ if (changed) {
161
+ writeFileSync(fullPath, content);
162
+ }
163
+ }
164
+ catch {
165
+ // Skip binary files or files that can't be read as text
166
+ }
167
+ }
168
+ }
169
+ }
170
+ /**
171
+ * Recursively copy directory with placeholder replacement.
172
+ */
173
+ function copyTemplateDir(src, dest, replacements) {
174
+ mkdirSync(dest, { recursive: true });
175
+ const entries = readdirSync(src);
176
+ for (const entry of entries) {
177
+ const srcPath = join(src, entry);
178
+ const destPath = join(dest, entry);
179
+ const stat = statSync(srcPath);
180
+ if (stat.isDirectory()) {
181
+ copyTemplateDir(srcPath, destPath, replacements);
182
+ }
183
+ else {
184
+ let content = readFileSync(srcPath, "utf-8");
185
+ for (const [placeholder, value] of Object.entries(replacements)) {
186
+ content = content.replace(new RegExp(placeholder, "g"), value);
187
+ }
188
+ writeFileSync(destPath, content);
189
+ }
190
+ }
191
+ }
192
+ /**
193
+ * Initialize a git repository in the scaffolded project.
194
+ */
195
+ async function initGitRepository(projectPath, templateName) {
196
+ try {
197
+ // Check if git is available
198
+ try {
199
+ execSync("git --version", { stdio: "pipe" });
200
+ }
201
+ catch {
202
+ return;
203
+ }
204
+ // Skip if already in a git repo
205
+ try {
206
+ execSync("git rev-parse --git-dir", { stdio: "pipe", cwd: projectPath });
207
+ return;
208
+ }
209
+ catch {
210
+ // Not in a git repo - proceed
211
+ }
212
+ execFileSync("git", ["init"], { stdio: "pipe", cwd: projectPath });
213
+ execFileSync("git", ["add", "-A"], { stdio: "pipe", cwd: projectPath });
214
+ execFileSync("git", ["commit", "-m", `chore: scaffold ${templateName} with DoSwiftly CLI`], {
215
+ stdio: "pipe",
216
+ cwd: projectPath,
217
+ });
218
+ p.log.info(pc.dim("Git repository initialized with initial commit."));
219
+ }
220
+ catch {
221
+ // Non-fatal
222
+ }
223
+ }
224
+ // ============================================================================
225
+ // File Creation Utilities
226
+ // ============================================================================
227
+ /**
228
+ * Create .env.local file
229
+ */
230
+ function createEnvLocal(targetDir, shopSlug, apiUrl, projectName) {
231
+ const envContent = [
232
+ "# Generated by DoSwiftly CLI",
233
+ `# Project: ${projectName}`,
234
+ "",
235
+ `NEXT_PUBLIC_API_URL=${apiUrl}`,
236
+ `NEXT_PUBLIC_SHOP_SLUG=${shopSlug}`,
237
+ "",
238
+ "# Site Configuration",
239
+ "NEXT_PUBLIC_SITE_URL=http://localhost:3000",
240
+ `NEXT_PUBLIC_SITE_NAME=${projectName}`,
241
+ "",
242
+ ].join("\n");
243
+ writeFileSync(join(targetDir, ".env.local"), envContent);
244
+ }
245
+ /**
246
+ * Create doswiftly.config.ts file
247
+ */
248
+ function createDoswiftlyConfig(targetDir, shopSlug, apiUrl, projectName) {
249
+ const configContent = [
250
+ "// Storefront configuration",
251
+ "// This file is used by both CLI and runtime code",
252
+ "",
253
+ "export interface DoswiftlyConfig {",
254
+ " shop: {",
255
+ " slug: string;",
256
+ " };",
257
+ " project: {",
258
+ " name: string;",
259
+ " };",
260
+ " api: {",
261
+ " url: string;",
262
+ " };",
263
+ " dev?: {",
264
+ " port?: number;",
265
+ " openBrowser?: boolean;",
266
+ " };",
267
+ "}",
268
+ "",
269
+ "const config: DoswiftlyConfig = {",
270
+ " shop: {",
271
+ ` slug: '${shopSlug}',`,
272
+ " },",
273
+ " project: {",
274
+ ` name: '${projectName}',`,
275
+ " },",
276
+ " api: {",
277
+ ` url: '${apiUrl}',`,
278
+ " },",
279
+ " dev: {",
280
+ " port: 3000,",
281
+ " openBrowser: true,",
282
+ " },",
283
+ "};",
284
+ "",
285
+ "export default config;",
286
+ "",
287
+ ].join("\n");
288
+ writeFileSync(join(targetDir, "doswiftly.config.ts"), configContent);
289
+ }
290
+ // ============================================================================
291
+ // Wizard Steps
292
+ // ============================================================================
293
+ /**
294
+ * Step 1: Target directory selection
295
+ */
296
+ const stepDirectory = {
297
+ id: "directory",
298
+ title: () => t().targetDirectory,
299
+ stateKeys: ["targetDir", "projectName"],
300
+ canGoBack: false, // First step, can't go back
301
+ async execute(state, ctx) {
302
+ // Use a loop to handle validation errors without losing context
303
+ while (true) {
304
+ const choice = await p.select({
305
+ message: t().targetDirectory,
306
+ options: [
307
+ {
308
+ value: "current",
309
+ label: t().currentDirectory(basename(process.cwd())),
310
+ },
311
+ { value: "new", label: t().newDirectory },
312
+ ],
313
+ });
314
+ if (p.isCancel(choice))
315
+ return cancel();
316
+ if (choice === "current") {
317
+ const cwd = process.cwd();
318
+ const projectName = basename(cwd);
319
+ // Validate: directory should be empty or contain only hidden files
320
+ const entries = readdirSync(cwd).filter((e) => !e.startsWith("."));
321
+ if (entries.length > 0) {
322
+ // Clear screen and show error with option to retry
323
+ clearScreen();
324
+ p.intro(pc.bgCyan(pc.black(` ${t().wizardTitle} `)));
325
+ const current = ctx.stepIndex + 1;
326
+ const total = ctx.totalSteps;
327
+ const filled = pc.green('●'.repeat(current));
328
+ const empty = pc.dim('○'.repeat(total - current));
329
+ p.log.step(pc.cyan(`${t().stepOf(current, total)}: ${t().targetDirectory}`));
330
+ p.log.message(`${filled}${empty}`, { symbol: '' });
331
+ p.log.error(t().directoryNotEmpty);
332
+ p.log.info(pc.dim(t().chooseNewDirectory));
333
+ const retry = await p.confirm({
334
+ message: t().tryAgain,
335
+ initialValue: true,
336
+ });
337
+ if (p.isCancel(retry))
338
+ return cancel();
339
+ if (!retry)
340
+ return cancel();
341
+ // Clear screen and redraw for retry
342
+ clearScreen();
343
+ p.intro(pc.bgCyan(pc.black(` ${t().wizardTitle} `)));
344
+ p.log.step(pc.cyan(`${t().stepOf(current, total)}: ${t().targetDirectory}`));
345
+ p.log.message(`${filled}${empty}`, { symbol: '' });
346
+ continue; // Loop back to selection
347
+ }
348
+ // Check if already initialized
349
+ if (existsSync(join(cwd, "doswiftly.config.ts")) ||
350
+ existsSync(join(cwd, "doswiftly.config.js"))) {
351
+ // Clear screen and show error
352
+ clearScreen();
353
+ p.intro(pc.bgCyan(pc.black(` ${t().wizardTitle} `)));
354
+ const current = ctx.stepIndex + 1;
355
+ const total = ctx.totalSteps;
356
+ const filled = pc.green('●'.repeat(current));
357
+ const empty = pc.dim('○'.repeat(total - current));
358
+ p.log.step(pc.cyan(`${t().stepOf(current, total)}: ${t().targetDirectory}`));
359
+ p.log.message(`${filled}${empty}`, { symbol: '' });
360
+ p.log.error(t().alreadyInitialized);
361
+ p.log.info(pc.dim(t().useDeploy));
362
+ const retry = await p.confirm({
363
+ message: t().tryAgain,
364
+ initialValue: true,
365
+ });
366
+ if (p.isCancel(retry))
367
+ return cancel();
368
+ if (!retry)
369
+ return cancel();
370
+ // Clear screen and redraw for retry
371
+ clearScreen();
372
+ p.intro(pc.bgCyan(pc.black(` ${t().wizardTitle} `)));
373
+ p.log.step(pc.cyan(`${t().stepOf(current, total)}: ${t().targetDirectory}`));
374
+ p.log.message(`${filled}${empty}`, { symbol: '' });
375
+ continue; // Loop back to selection
376
+ }
377
+ return next({ targetDir: cwd, projectName });
378
+ }
379
+ // "new" was selected, break out of loop
380
+ break;
381
+ }
382
+ // New directory flow
383
+ const projectName = await p.text({
384
+ message: t().directoryName,
385
+ validate: (v) => {
386
+ if (!v)
387
+ return t().nameRequired;
388
+ if (!/^[a-z0-9-]+$/.test(v))
389
+ return t().invalidName;
390
+ if (existsSync(join(process.cwd(), v)))
391
+ return t().directoryExists;
392
+ return undefined;
393
+ },
394
+ });
395
+ if (p.isCancel(projectName))
396
+ return cancel();
397
+ return next({
398
+ targetDir: join(process.cwd(), projectName),
399
+ projectName: projectName,
400
+ });
401
+ },
402
+ };
403
+ /**
404
+ * Step 2: Team selection
405
+ */
406
+ const stepTeam = {
407
+ id: "team",
408
+ title: () => t().selectTeam,
409
+ stateKeys: ["team"],
410
+ canGoBack: true,
411
+ async execute(state, ctx) {
412
+ const teams = state.__teams;
413
+ // Single team: ask for confirmation instead of auto-selecting
414
+ if (teams.length === 1) {
415
+ const proceed = await p.confirm({
416
+ message: t().useTeam(teams[0].name),
417
+ initialValue: true,
418
+ });
419
+ if (p.isCancel(proceed))
420
+ return cancel();
421
+ if (!proceed)
422
+ return back();
423
+ return next({ team: teams[0] });
424
+ }
425
+ // Multiple teams: show select with back navigation
426
+ const teamId = await selectWithBack({
427
+ message: t().selectTeam,
428
+ options: teams.map((team) => ({
429
+ value: team.id,
430
+ label: team.name,
431
+ hint: t().teamProjects(team.projectCount),
432
+ })),
433
+ canGoBack: ctx.canGoBack,
434
+ });
435
+ if (isCancel(teamId))
436
+ return cancel();
437
+ if (isBack(teamId))
438
+ return back();
439
+ const selectedTeam = teams.find((team) => team.id === teamId);
440
+ return next({ team: selectedTeam });
441
+ },
442
+ };
443
+ /**
444
+ * Step 3: Project selection
445
+ */
446
+ const stepProject = {
447
+ id: "project",
448
+ title: () => t().selectProject,
449
+ stateKeys: ["project", "shopSlug"],
450
+ canGoBack: true,
451
+ async execute(state, ctx) {
452
+ const teamProjects = state.__projects.filter((p) => p.teamId === state.team.id);
453
+ if (teamProjects.length === 0) {
454
+ // Clear screen and redraw header for consistent UI
455
+ clearScreen();
456
+ p.intro(pc.bgCyan(pc.black(` ${t().wizardTitle} `)));
457
+ // Show progress
458
+ const current = ctx.stepIndex + 1;
459
+ const total = ctx.totalSteps;
460
+ const filled = pc.green('●'.repeat(current));
461
+ const empty = pc.dim('○'.repeat(total - current));
462
+ p.log.step(pc.cyan(`${t().stepOf(current, total)}: ${t().selectProject}`));
463
+ p.log.message(`${filled}${empty}`, { symbol: '' });
464
+ p.log.error(t().noProjects);
465
+ p.log.info(pc.cyan(t().createProjectHint));
466
+ // Allow going back to select different team
467
+ const goBack = await p.confirm({
468
+ message: t().goBackToTeam,
469
+ initialValue: true,
470
+ });
471
+ if (p.isCancel(goBack))
472
+ return cancel();
473
+ if (goBack)
474
+ return back();
475
+ // User chose not to go back
476
+ return cancel();
477
+ }
478
+ const projectId = await selectWithBack({
479
+ message: t().selectProject,
480
+ options: teamProjects.map((proj) => ({
481
+ value: proj.id,
482
+ label: proj.name,
483
+ hint: proj.hasShop ? t().shopLabel(proj.shop.slug) : t().noShop,
484
+ })),
485
+ canGoBack: ctx.canGoBack,
486
+ });
487
+ if (isCancel(projectId))
488
+ return cancel();
489
+ if (isBack(projectId))
490
+ return back();
491
+ const selectedProject = teamProjects.find((p) => p.id === projectId);
492
+ // Validate shop exists
493
+ if (!selectedProject?.hasShop || !selectedProject.shop) {
494
+ // Clear screen and redraw header for consistent UI
495
+ clearScreen();
496
+ p.intro(pc.bgCyan(pc.black(` ${t().wizardTitle} `)));
497
+ // Show progress
498
+ const current = ctx.stepIndex + 1;
499
+ const total = ctx.totalSteps;
500
+ const filled = pc.green('●'.repeat(current));
501
+ const empty = pc.dim('○'.repeat(total - current));
502
+ p.log.step(pc.cyan(`${t().stepOf(current, total)}: ${t().selectProject}`));
503
+ p.log.message(`${filled}${empty}`, { symbol: '' });
504
+ p.log.error(t().projectNoShop);
505
+ p.log.info(pc.cyan(t().createShopHint(selectedProject.id)));
506
+ // Allow going back instead of exiting
507
+ const retry = await p.confirm({
508
+ message: t().selectAnother,
509
+ initialValue: true,
510
+ });
511
+ if (p.isCancel(retry))
512
+ return cancel();
513
+ if (retry)
514
+ return back();
515
+ // User chose not to retry
516
+ return cancel();
517
+ }
518
+ return next({
519
+ project: selectedProject,
520
+ shopSlug: selectedProject.shop.slug,
521
+ });
522
+ },
523
+ };
524
+ /**
525
+ * Step 4: Code source selection
526
+ *
527
+ * This step handles both the source type selection AND template selection
528
+ * in a single wizard step. Uses internal loop for proper back navigation:
529
+ * - Back from template list → returns to "Empty/Template" prompt (same step)
530
+ * - Back from "Empty/Template" → returns to previous wizard step (Project)
531
+ */
532
+ const stepCodeSource = {
533
+ id: "codeSource",
534
+ title: () => t().codeSource,
535
+ stateKeys: ["codeSource"],
536
+ canGoBack: true,
537
+ async execute(state, ctx) {
538
+ // Loop to handle internal back navigation within this step
539
+ while (true) {
540
+ // First prompt: choose source type
541
+ const choice = await selectWithBack({
542
+ message: t().codeSource,
543
+ options: [
544
+ { value: "empty", label: t().emptyProject },
545
+ { value: "template", label: t().templateRegistry },
546
+ ],
547
+ canGoBack: ctx.canGoBack,
548
+ });
549
+ if (isCancel(choice))
550
+ return cancel();
551
+ if (isBack(choice))
552
+ return back(); // Goes to previous wizard step (Project)
553
+ if (choice === "empty") {
554
+ return next({ codeSource: { type: "empty" } });
555
+ }
556
+ // User chose template - fetch and show template list
557
+ const spinner = ora(t().fetchingTemplates).start();
558
+ const templates = await fetchRemoteTemplates(state.__apiUrl, state.__token);
559
+ spinner.stop();
560
+ if (!templates || templates.length === 0) {
561
+ p.log.warn(t().noTemplates);
562
+ return next({ codeSource: { type: "empty" } });
563
+ }
564
+ // Clear screen for clean template selection UI
565
+ clearScreen();
566
+ p.intro(pc.bgCyan(pc.black(` ${t().wizardTitle} `)));
567
+ // Show progress - template path adds +1 step (5/6 instead of 4/5)
568
+ const templateCurrent = ctx.stepIndex + 2; // 5
569
+ const templateTotal = ctx.totalSteps + 1; // 6
570
+ const filledTemplate = pc.green('●'.repeat(templateCurrent));
571
+ const emptyTemplate = pc.dim('○'.repeat(templateTotal - templateCurrent));
572
+ p.log.step(pc.cyan(`${t().stepOf(templateCurrent, templateTotal)}: ${t().selectTemplate}`));
573
+ p.log.message(`${filledTemplate}${emptyTemplate}`, { symbol: '' });
574
+ // Second prompt: select specific template
575
+ const templateId = await selectWithBack({
576
+ message: t().selectTemplate,
577
+ options: templates.map((tpl) => ({
578
+ value: tpl.id,
579
+ label: `${tpl.name}${tpl.uiLibrary ? ` (${tpl.uiLibrary})` : ""}`,
580
+ hint: tpl.description || undefined,
581
+ })),
582
+ canGoBack: true,
583
+ });
584
+ if (isCancel(templateId))
585
+ return cancel();
586
+ if (isBack(templateId)) {
587
+ // Back from template list → loop back to first prompt (Empty/Template)
588
+ // Clear screen and redraw the step header before looping
589
+ // Show 4/5 (default total) since user hasn't chosen path yet
590
+ const backCurrent = ctx.stepIndex + 1; // 4
591
+ const backTotal = ctx.totalSteps; // 5 (base)
592
+ const filledBack = pc.green('●'.repeat(backCurrent));
593
+ const emptyBack = pc.dim('○'.repeat(backTotal - backCurrent));
594
+ clearScreen();
595
+ p.intro(pc.bgCyan(pc.black(` ${t().wizardTitle} `)));
596
+ p.log.step(pc.cyan(`${t().stepOf(backCurrent, backTotal)}: ${t().codeSource}`));
597
+ p.log.message(`${filledBack}${emptyBack}`, { symbol: '' });
598
+ continue; // Go back to start of loop (first prompt)
599
+ }
600
+ const selectedTemplate = templates.find((tpl) => tpl.id === templateId);
601
+ if (!selectedTemplate?.repoUrl) {
602
+ p.log.warn(t().templateNoRepo);
603
+ return next({ codeSource: { type: "empty" } });
604
+ }
605
+ return next({
606
+ codeSource: {
607
+ type: "template",
608
+ templateRepoUrl: selectedTemplate.repoUrl,
609
+ templateName: selectedTemplate.name,
610
+ },
611
+ });
612
+ }
613
+ },
614
+ };
615
+ /**
616
+ * Step 5 (or 6 for template path): Confirmation and dependencies
617
+ */
618
+ const stepConfirm = {
619
+ id: "confirm",
620
+ title: () => t().summary,
621
+ stateKeys: ["shouldInstall"],
622
+ canGoBack: true,
623
+ // Dynamic progress: 5/5 for empty path, 6/6 for template path
624
+ getProgress(state, defaultProgress) {
625
+ if (state.codeSource?.type === "template") {
626
+ return {
627
+ current: defaultProgress.current + 1, // 6
628
+ total: defaultProgress.total + 1, // 6
629
+ };
630
+ }
631
+ return defaultProgress; // 5/5 for empty
632
+ },
633
+ async execute(state, ctx) {
634
+ // Display summary with consistent alignment
635
+ const templateName = state.codeSource.type === "empty"
636
+ ? "Minimal"
637
+ : state.codeSource.templateName || "Template";
638
+ const summaryLines = [
639
+ ` ${pc.dim("Directory:")} ${state.targetDir}`,
640
+ ` ${pc.dim("Team:")} ${state.team.name}`,
641
+ ` ${pc.dim("Project:")} ${state.project.name}`,
642
+ ` ${pc.dim("Shop:")} ${state.shopSlug}`,
643
+ ` ${pc.dim("Template:")} ${templateName}`,
644
+ ];
645
+ console.log(); // Empty line before summary
646
+ console.log(pc.bold(t().summary + ":"));
647
+ summaryLines.forEach(line => console.log(line));
648
+ console.log(); // Empty line after summary
649
+ const confirmChoice = await selectWithBack({
650
+ message: t().continue,
651
+ options: [
652
+ { value: "yes", label: t().yes },
653
+ { value: "no", label: t().cancel },
654
+ ],
655
+ canGoBack: ctx.canGoBack,
656
+ });
657
+ if (isCancel(confirmChoice))
658
+ return cancel();
659
+ if (isBack(confirmChoice))
660
+ return back();
661
+ if (confirmChoice === "no") {
662
+ p.cancel(t().cancelled);
663
+ process.exit(0);
664
+ }
665
+ // Ask about installing dependencies
666
+ const installChoice = await p.select({
667
+ message: t().installDeps,
668
+ options: [
669
+ { value: "yes", label: t().yesInstall(state.__pmInfo.name) },
670
+ { value: "no", label: t().noInstallLater },
671
+ ],
672
+ });
673
+ if (p.isCancel(installChoice))
674
+ return cancel();
675
+ return next({ shouldInstall: installChoice === "yes" });
676
+ },
677
+ };
678
+ // ============================================================================
679
+ // Template Creation Flow (for SaaS developers)
680
+ // ============================================================================
681
+ /**
682
+ * Create a new template project for the registry (SaaS developer flow).
683
+ */
684
+ async function initTemplateProject(options) {
685
+ p.intro(pc.bgCyan(pc.black(" DoSwiftly CLI - Create Template ")));
686
+ const { getAuthStateSilent } = await import("./auth.js");
687
+ const { isSaasDeveloper } = getAuthStateSilent();
688
+ if (!isSaasDeveloper) {
689
+ p.log.warn("Warning: You don't appear to be a SaaS developer.");
690
+ p.log.info(pc.dim("Template creation works locally, but you'll need the SaaS developer role to register."));
691
+ }
692
+ // Template name
693
+ const templateName = options.name ||
694
+ (await p.text({
695
+ message: "Template name:",
696
+ validate: (v) => {
697
+ if (!v)
698
+ return "Name is required";
699
+ if (!/^[a-z0-9-]+$/.test(v))
700
+ return "Use lowercase letters, numbers, and hyphens only";
701
+ if (v.length < 3)
702
+ return "At least 3 characters";
703
+ return undefined;
704
+ },
705
+ }));
706
+ if (p.isCancel(templateName)) {
707
+ p.cancel(t().cancelled);
708
+ process.exit(0);
709
+ }
710
+ // Template description
711
+ const description = await p.text({
712
+ message: "Template description (optional):",
713
+ placeholder: `DoSwiftly storefront template: ${templateName}`,
714
+ });
715
+ if (p.isCancel(description)) {
716
+ p.cancel(t().cancelled);
717
+ process.exit(0);
718
+ }
719
+ // Base template selection
720
+ const baseTemplate = await p.select({
721
+ message: "Base template to start from:",
722
+ options: [
723
+ {
724
+ value: "storefront-nextjs-shadcn",
725
+ label: "shadcn/ui (Recommended)",
726
+ hint: "Next.js + shadcn/ui + Tailwind CSS",
727
+ },
728
+ {
729
+ value: "storefront-minimal",
730
+ label: "Minimal (Next.js only)",
731
+ hint: "Minimal Next.js starter",
732
+ },
733
+ ],
734
+ });
735
+ if (p.isCancel(baseTemplate)) {
736
+ p.cancel(t().cancelled);
737
+ process.exit(0);
738
+ }
739
+ // UI Library label
740
+ const uiLibraryMap = {
741
+ "storefront-nextjs-shadcn": "shadcn/ui",
742
+ "storefront-minimal": "none",
743
+ };
744
+ const uiLibrary = uiLibraryMap[baseTemplate] || "none";
745
+ // Create directory
746
+ const targetDir = templateName;
747
+ if (existsSync(targetDir)) {
748
+ p.log.error(`Directory "${targetDir}" already exists.`);
749
+ process.exit(1);
750
+ }
751
+ const spinner = ora("Creating template project...").start();
752
+ try {
753
+ const templateDir = join(__dirname, "..", "..", "templates", baseTemplate);
754
+ if (!existsSync(templateDir)) {
755
+ throw new Error(`Base template not found: ${baseTemplate}`);
756
+ }
757
+ // Copy template files (with generic placeholders)
758
+ const replacements = {
759
+ "\\{\\{PROJECT_NAME\\}\\}": templateName,
760
+ "\\{\\{SHOP_SLUG\\}\\}": "{{SHOP_SLUG}}",
761
+ "\\{\\{API_URL\\}\\}": "{{API_URL}}",
762
+ "\\{\\{SDK_VERSION\\}\\}": "^1.0.0",
763
+ "\\{\\{COMMERCE_SDK_VERSION\\}\\}": "^1.0.0",
764
+ "\\{\\{STOREFRONT_OPS_VERSION\\}\\}": "^1.0.0",
765
+ };
766
+ copyTemplateDir(templateDir, targetDir, replacements);
767
+ // Create doswiftly-template.json manifest
768
+ const manifest = {
769
+ name: templateName,
770
+ description: description || `DoSwiftly storefront template: ${templateName}`,
771
+ uiLibrary,
772
+ features: [],
773
+ version: "0.1.0",
774
+ };
775
+ writeFileSync(join(targetDir, "doswiftly-template.json"), JSON.stringify(manifest, null, 2) + "\n");
776
+ // Initialize git
777
+ spinner.text = "Initializing git repository...";
778
+ try {
779
+ execSync("git init", { stdio: "pipe", cwd: targetDir });
780
+ execSync("git add -A", { stdio: "pipe", cwd: targetDir });
781
+ execSync(`git commit -m "chore: initialize ${templateName} template"`, {
782
+ stdio: "pipe",
783
+ cwd: targetDir,
784
+ });
785
+ }
786
+ catch {
787
+ // Non-fatal
788
+ }
789
+ spinner.succeed(pc.green("Template project created!"));
790
+ p.outro(pc.green("Success!"));
791
+ p.log.info(pc.dim(`Created template project in: ${pc.cyan(targetDir)}/`));
792
+ p.log.message("");
793
+ p.log.message(pc.bold("Next steps:"));
794
+ p.log.message(pc.cyan(` cd ${templateName}`));
795
+ p.log.message(pc.dim(" # Customize the template to your needs"));
796
+ p.log.message(pc.cyan(" git remote add origin <your-github-repo-url>"));
797
+ p.log.message(pc.cyan(" git push -u origin main"));
798
+ p.log.message(pc.cyan(" doswiftly template register"));
799
+ p.log.message("");
800
+ }
801
+ catch (error) {
802
+ spinner.fail(pc.red("Failed to create template project"));
803
+ console.error(pc.red(error.message));
804
+ process.exit(1);
805
+ }
806
+ }
807
+ // ============================================================================
808
+ // Project Creation
809
+ // ============================================================================
810
+ /**
811
+ * Execute project creation after wizard completes
812
+ */
813
+ async function createProject(state) {
814
+ const { targetDir, projectName, shopSlug, codeSource, shouldInstall, __apiUrl: apiUrl, __pmInfo: pmInfo, } = state;
815
+ // Clear screen for clean project creation output
816
+ clearScreen();
817
+ p.intro(pc.bgCyan(pc.black(` ${t().wizardTitle} `)));
818
+ const createSpinner = ora(t().creatingProject).start();
819
+ try {
820
+ // Create target directory if it doesn't exist
821
+ if (!existsSync(targetDir)) {
822
+ mkdirSync(targetDir, { recursive: true });
823
+ }
824
+ // Define placeholder replacements
825
+ const replacements = {
826
+ "\\{\\{PROJECT_NAME\\}\\}": projectName,
827
+ "\\{\\{SHOP_SLUG\\}\\}": shopSlug,
828
+ "\\{\\{API_URL\\}\\}": apiUrl,
829
+ "\\{\\{SDK_VERSION\\}\\}": "^1.0.0",
830
+ "\\{\\{COMMERCE_SDK_VERSION\\}\\}": "^1.0.0",
831
+ "\\{\\{STOREFRONT_OPS_VERSION\\}\\}": "^1.0.0",
832
+ };
833
+ let templateName = "storefront-minimal";
834
+ if (codeSource.type === "template" && codeSource.templateRepoUrl) {
835
+ // Download remote template
836
+ createSpinner.text = t().downloadingTemplate;
837
+ await downloadTemplate(codeSource.templateRepoUrl, targetDir);
838
+ createSpinner.text = t().configuringProject;
839
+ applyReplacementsToDirectory(targetDir, replacements);
840
+ templateName = codeSource.templateName || "remote-template";
841
+ }
842
+ else {
843
+ // Copy minimal template
844
+ createSpinner.text = t().copyingTemplate;
845
+ const minimalTemplate = join(__dirname, "..", "..", "templates", "storefront-minimal");
846
+ if (!existsSync(minimalTemplate)) {
847
+ throw new Error("Minimal template not found. Please reinstall @doswiftly/cli.");
848
+ }
849
+ copyTemplateDir(minimalTemplate, targetDir, replacements);
850
+ }
851
+ // Create .env.local
852
+ createSpinner.text = t().creatingEnvLocal;
853
+ createEnvLocal(targetDir, shopSlug, apiUrl, projectName);
854
+ // Create doswiftly.config.ts
855
+ createSpinner.text = t().creatingConfig;
856
+ createDoswiftlyConfig(targetDir, shopSlug, apiUrl, projectName);
857
+ // Create .doswiftly directory for cache
858
+ mkdirSync(join(targetDir, ".doswiftly"), { recursive: true });
859
+ writeFileSync(join(targetDir, ".doswiftly", ".gitignore"), "*\n");
860
+ // Create .gitignore if it doesn't exist (npm strips .gitignore from packages)
861
+ const gitignorePath = join(targetDir, ".gitignore");
862
+ if (!existsSync(gitignorePath)) {
863
+ const gitignoreContent = `# Dependencies
864
+ node_modules/
865
+ .pnpm-store/
866
+
867
+ # Next.js
868
+ .next/
869
+ out/
870
+
871
+ # Build
872
+ dist/
873
+ build/
874
+
875
+ # Environment files
876
+ .env
877
+ .env.local
878
+ .env.*.local
879
+
880
+ # Debug logs
881
+ npm-debug.log*
882
+ yarn-debug.log*
883
+ yarn-error.log*
884
+ pnpm-debug.log*
885
+
886
+ # IDE
887
+ .idea/
888
+ .vscode/
889
+ *.swp
890
+ *.swo
891
+
892
+ # OS
893
+ .DS_Store
894
+ Thumbs.db
895
+
896
+ # DoSwiftly cache
897
+ .doswiftly/
898
+
899
+ # TypeScript
900
+ *.tsbuildinfo
901
+
902
+ # Testing
903
+ coverage/
904
+ `;
905
+ writeFileSync(gitignorePath, gitignoreContent);
906
+ }
907
+ // Auto-create a default profile if none exists
908
+ if (!hasProfiles()) {
909
+ try {
910
+ addProfile({
911
+ name: "default",
912
+ apiUrl: apiUrl,
913
+ description: `Auto-created by doswiftly init for ${projectName}`,
914
+ });
915
+ }
916
+ catch {
917
+ // Non-fatal
918
+ }
919
+ }
920
+ createSpinner.succeed(t().projectCreated);
921
+ // Install dependencies if requested
922
+ if (shouldInstall) {
923
+ const installSpinner = ora(t().installingDeps).start();
924
+ try {
925
+ await installDependencies(pmInfo.name, [], {
926
+ stdio: "pipe",
927
+ cwd: targetDir,
928
+ });
929
+ installSpinner.succeed(t().depsInstalled);
930
+ }
931
+ catch (installError) {
932
+ installSpinner.fail(t().installFailed);
933
+ const errorMessage = installError instanceof Error
934
+ ? installError.message
935
+ : String(installError);
936
+ if (errorMessage) {
937
+ p.log.info(pc.dim(errorMessage.split("\n")[0]));
938
+ }
939
+ p.log.warn(t().runManually(projectName, pmInfo.name));
940
+ }
941
+ }
942
+ // Initialize git repository
943
+ await initGitRepository(targetDir, templateName);
944
+ // Success message
945
+ p.log.message("");
946
+ p.log.success(pc.green(t().projectReady));
947
+ p.log.message("");
948
+ p.log.message(pc.dim(t().nextSteps));
949
+ if (targetDir !== process.cwd()) {
950
+ p.log.message(pc.cyan(` cd ${projectName}`));
951
+ }
952
+ if (!shouldInstall) {
953
+ p.log.message(pc.cyan(` ${pmInfo.name} install`));
954
+ }
955
+ p.log.message(pc.cyan(" doswiftly dev"));
956
+ p.log.message("");
957
+ p.log.message(pc.dim(t().documentation));
958
+ p.log.message("");
959
+ }
960
+ catch (error) {
961
+ createSpinner.fail(pc.red(t().failedCreateProject));
962
+ console.error(pc.red(error.message));
963
+ process.exit(1);
964
+ }
965
+ }
966
+ // ============================================================================
967
+ // Main Init Command
968
+ // ============================================================================
969
+ export async function initCommand(options = {}) {
970
+ // Branch to template creation flow if --create-template is specified
971
+ if (options.createTemplate) {
972
+ return initTemplateProject(options);
973
+ }
974
+ // Check authentication first
975
+ const token = getStoredToken();
976
+ if (!token) {
977
+ p.intro(pc.bgRed(pc.white(" Error ")));
978
+ p.log.error(t().notLoggedIn);
979
+ p.log.info(pc.cyan(t().runAuthLogin));
980
+ process.exit(1);
981
+ }
982
+ const apiUrl = getApiUrl();
983
+ // Fetch data upfront
984
+ const spinner = ora(t().fetchingData).start();
985
+ const teams = await fetchTeams(apiUrl, token);
986
+ const projects = await fetchProjects(apiUrl, token);
987
+ spinner.stop();
988
+ // Check teams exist
989
+ if (teams.length === 0) {
990
+ p.intro(pc.bgRed(pc.white(" Error ")));
991
+ p.log.error(t().noTeams);
992
+ p.log.info(pc.cyan(t().createTeamHint));
993
+ process.exit(1);
994
+ }
995
+ // Detect package manager
996
+ const pmInfo = options.pm
997
+ ? await getPackageManagerInfo(options.pm)
998
+ : await detectPackageManager();
999
+ // Define wizard steps
1000
+ const steps = [
1001
+ stepDirectory,
1002
+ stepTeam,
1003
+ stepProject,
1004
+ stepCodeSource,
1005
+ stepConfirm,
1006
+ ];
1007
+ // Initial state with internal data
1008
+ const initialState = {
1009
+ __teams: teams,
1010
+ __projects: projects,
1011
+ __apiUrl: apiUrl,
1012
+ __token: token,
1013
+ __pmInfo: pmInfo,
1014
+ };
1015
+ // Run wizard
1016
+ const engine = new WizardEngine(steps, initialState, {
1017
+ title: t().wizardTitle,
1018
+ cancelMessage: t().cancelled,
1019
+ });
1020
+ const result = await engine.run();
1021
+ if (!result) {
1022
+ // Wizard was cancelled
1023
+ process.exit(0);
1024
+ }
1025
+ // Execute project creation with final state
1026
+ await createProject(result);
1027
+ }
1028
+ //# sourceMappingURL=init.js.map