@launch77/cli 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (398) hide show
  1. package/.eslintrc.json +6 -0
  2. package/dist/app-templates/webapp/.env.ci +6 -0
  3. package/dist/app-templates/webapp/.env.example +9 -0
  4. package/dist/app-templates/webapp/.eslintrc.json +6 -0
  5. package/dist/app-templates/webapp/README.md.hbs +80 -0
  6. package/dist/app-templates/webapp/app/about/page.tsx.hbs +41 -0
  7. package/dist/app-templates/webapp/app/dashboard/page.tsx.hbs +51 -0
  8. package/dist/app-templates/webapp/app/globals.css +31 -0
  9. package/dist/app-templates/webapp/app/layout.tsx.hbs +26 -0
  10. package/dist/app-templates/webapp/app/page.tsx.hbs +30 -0
  11. package/dist/app-templates/webapp/next.config.js +99 -0
  12. package/dist/app-templates/webapp/package.json.hbs +30 -0
  13. package/dist/app-templates/webapp/postcss.config.js +6 -0
  14. package/dist/app-templates/webapp/tailwind.config.ts +24 -0
  15. package/dist/app-templates/webapp/tsconfig.json +29 -0
  16. package/dist/app-templates/webapp/vercel.json.hbs +7 -0
  17. package/dist/cli.d.ts +3 -0
  18. package/dist/cli.d.ts.map +1 -0
  19. package/dist/cli.js +29 -0
  20. package/dist/cli.js.map +1 -0
  21. package/dist/infrastructure/filesystem.d.ts +41 -0
  22. package/dist/infrastructure/filesystem.d.ts.map +1 -0
  23. package/dist/infrastructure/filesystem.js +61 -0
  24. package/dist/infrastructure/filesystem.js.map +1 -0
  25. package/dist/infrastructure/npm.d.ts +9 -0
  26. package/dist/infrastructure/npm.d.ts.map +1 -0
  27. package/dist/infrastructure/npm.js +17 -0
  28. package/dist/infrastructure/npm.js.map +1 -0
  29. package/dist/infrastructure/template-engine.d.ts +12 -0
  30. package/dist/infrastructure/template-engine.d.ts.map +1 -0
  31. package/dist/infrastructure/template-engine.js +40 -0
  32. package/dist/infrastructure/template-engine.js.map +1 -0
  33. package/dist/infrastructure/template-generator.d.ts +9 -0
  34. package/dist/infrastructure/template-generator.d.ts.map +1 -0
  35. package/dist/infrastructure/template-generator.js +19 -0
  36. package/dist/infrastructure/template-generator.js.map +1 -0
  37. package/dist/infrastructure/template.d.ts +20 -0
  38. package/dist/infrastructure/template.d.ts.map +1 -0
  39. package/dist/infrastructure/template.js +53 -0
  40. package/dist/infrastructure/template.js.map +1 -0
  41. package/dist/modules/app/commands/create-app.d.ts +3 -0
  42. package/dist/modules/app/commands/create-app.d.ts.map +1 -0
  43. package/dist/modules/app/commands/create-app.js +51 -0
  44. package/dist/modules/app/commands/create-app.js.map +1 -0
  45. package/dist/modules/app/commands/delete-app.d.ts +3 -0
  46. package/dist/modules/app/commands/delete-app.d.ts.map +1 -0
  47. package/dist/modules/app/commands/delete-app.js +109 -0
  48. package/dist/modules/app/commands/delete-app.js.map +1 -0
  49. package/dist/modules/app/commands/generate-manifest.d.ts +3 -0
  50. package/dist/modules/app/commands/generate-manifest.d.ts.map +1 -0
  51. package/dist/modules/app/commands/generate-manifest.js +62 -0
  52. package/dist/modules/app/commands/generate-manifest.js.map +1 -0
  53. package/dist/modules/app/commands/validate-manifest.d.ts +3 -0
  54. package/dist/modules/app/commands/validate-manifest.d.ts.map +1 -0
  55. package/dist/modules/app/commands/validate-manifest.js +68 -0
  56. package/dist/modules/app/commands/validate-manifest.js.map +1 -0
  57. package/dist/modules/app/errors/app-errors.d.ts +19 -0
  58. package/dist/modules/app/errors/app-errors.d.ts.map +1 -0
  59. package/dist/modules/app/errors/app-errors.js +37 -0
  60. package/dist/modules/app/errors/app-errors.js.map +1 -0
  61. package/dist/modules/app/index.d.ts +10 -0
  62. package/dist/modules/app/index.d.ts.map +1 -0
  63. package/dist/modules/app/index.js +11 -0
  64. package/dist/modules/app/index.js.map +1 -0
  65. package/dist/modules/app/lib/manifest-schema.d.ts +39 -0
  66. package/dist/modules/app/lib/manifest-schema.d.ts.map +1 -0
  67. package/dist/modules/app/lib/manifest-schema.js +44 -0
  68. package/dist/modules/app/lib/manifest-schema.js.map +1 -0
  69. package/dist/modules/app/services/app-svc.d.ts +15 -0
  70. package/dist/modules/app/services/app-svc.d.ts.map +1 -0
  71. package/dist/modules/app/services/app-svc.js +90 -0
  72. package/dist/modules/app/services/app-svc.js.map +1 -0
  73. package/dist/modules/app/services/manifest-svc.d.ts +28 -0
  74. package/dist/modules/app/services/manifest-svc.d.ts.map +1 -0
  75. package/dist/modules/app/services/manifest-svc.js +119 -0
  76. package/dist/modules/app/services/manifest-svc.js.map +1 -0
  77. package/dist/modules/app/types/app-types.d.ts +21 -0
  78. package/dist/modules/app/types/app-types.d.ts.map +1 -0
  79. package/dist/modules/app/types/app-types.js +5 -0
  80. package/dist/modules/app/types/app-types.js.map +1 -0
  81. package/dist/modules/catalog/commands/generate.d.ts +9 -0
  82. package/dist/modules/catalog/commands/generate.d.ts.map +1 -0
  83. package/dist/modules/catalog/commands/generate.js +94 -0
  84. package/dist/modules/catalog/commands/generate.js.map +1 -0
  85. package/dist/modules/catalog/commands/scan.d.ts +9 -0
  86. package/dist/modules/catalog/commands/scan.d.ts.map +1 -0
  87. package/dist/modules/catalog/commands/scan.js +43 -0
  88. package/dist/modules/catalog/commands/scan.js.map +1 -0
  89. package/dist/modules/catalog/config/catalog-config.d.ts +49 -0
  90. package/dist/modules/catalog/config/catalog-config.d.ts.map +1 -0
  91. package/dist/modules/catalog/config/catalog-config.js +116 -0
  92. package/dist/modules/catalog/config/catalog-config.js.map +1 -0
  93. package/dist/modules/catalog/config/catalog-config.test.d.ts +2 -0
  94. package/dist/modules/catalog/config/catalog-config.test.d.ts.map +1 -0
  95. package/dist/modules/catalog/config/catalog-config.test.js +362 -0
  96. package/dist/modules/catalog/config/catalog-config.test.js.map +1 -0
  97. package/dist/modules/catalog/index.d.ts +9 -0
  98. package/dist/modules/catalog/index.d.ts.map +1 -0
  99. package/dist/modules/catalog/index.js +9 -0
  100. package/dist/modules/catalog/index.js.map +1 -0
  101. package/dist/modules/catalog/parsers/typescript-parser.d.ts +15 -0
  102. package/dist/modules/catalog/parsers/typescript-parser.d.ts.map +1 -0
  103. package/dist/modules/catalog/parsers/typescript-parser.js +82 -0
  104. package/dist/modules/catalog/parsers/typescript-parser.js.map +1 -0
  105. package/dist/modules/catalog/scanners/metadata-extractor.d.ts +10 -0
  106. package/dist/modules/catalog/scanners/metadata-extractor.d.ts.map +1 -0
  107. package/dist/modules/catalog/scanners/metadata-extractor.js +122 -0
  108. package/dist/modules/catalog/scanners/metadata-extractor.js.map +1 -0
  109. package/dist/modules/catalog/scanners/metadata-validator.d.ts +6 -0
  110. package/dist/modules/catalog/scanners/metadata-validator.d.ts.map +1 -0
  111. package/dist/modules/catalog/scanners/metadata-validator.js +57 -0
  112. package/dist/modules/catalog/scanners/metadata-validator.js.map +1 -0
  113. package/dist/modules/catalog/scanners/ui-component-scanner.d.ts +11 -0
  114. package/dist/modules/catalog/scanners/ui-component-scanner.d.ts.map +1 -0
  115. package/dist/modules/catalog/scanners/ui-component-scanner.js +147 -0
  116. package/dist/modules/catalog/scanners/ui-component-scanner.js.map +1 -0
  117. package/dist/modules/catalog/schemas/catalog-ui-components.schema.json +161 -0
  118. package/dist/modules/catalog/schemas/schemas/catalog-ui-components.schema.json +145 -0
  119. package/dist/modules/catalog/services/catalog-generator-svc.d.ts +27 -0
  120. package/dist/modules/catalog/services/catalog-generator-svc.d.ts.map +1 -0
  121. package/dist/modules/catalog/services/catalog-generator-svc.js +100 -0
  122. package/dist/modules/catalog/services/catalog-generator-svc.js.map +1 -0
  123. package/dist/modules/catalog/services/catalog-generator-svc.test.d.ts +2 -0
  124. package/dist/modules/catalog/services/catalog-generator-svc.test.d.ts.map +1 -0
  125. package/dist/modules/catalog/services/catalog-generator-svc.test.js +201 -0
  126. package/dist/modules/catalog/services/catalog-generator-svc.test.js.map +1 -0
  127. package/dist/modules/catalog/services/catalog-svc.d.ts +39 -0
  128. package/dist/modules/catalog/services/catalog-svc.d.ts.map +1 -0
  129. package/dist/modules/catalog/services/catalog-svc.js +163 -0
  130. package/dist/modules/catalog/services/catalog-svc.js.map +1 -0
  131. package/dist/modules/catalog/types/catalog-types.d.ts +120 -0
  132. package/dist/modules/catalog/types/catalog-types.d.ts.map +1 -0
  133. package/dist/modules/catalog/types/catalog-types.js +5 -0
  134. package/dist/modules/catalog/types/catalog-types.js.map +1 -0
  135. package/dist/modules/catalog/utils/examples-generator.d.ts +6 -0
  136. package/dist/modules/catalog/utils/examples-generator.d.ts.map +1 -0
  137. package/dist/modules/catalog/utils/examples-generator.js +55 -0
  138. package/dist/modules/catalog/utils/examples-generator.js.map +1 -0
  139. package/dist/modules/catalog/utils/quality-reporter.d.ts +13 -0
  140. package/dist/modules/catalog/utils/quality-reporter.d.ts.map +1 -0
  141. package/dist/modules/catalog/utils/quality-reporter.js +64 -0
  142. package/dist/modules/catalog/utils/quality-reporter.js.map +1 -0
  143. package/dist/modules/catalog/validators/component-name-validator.d.ts +20 -0
  144. package/dist/modules/catalog/validators/component-name-validator.d.ts.map +1 -0
  145. package/dist/modules/catalog/validators/component-name-validator.js +33 -0
  146. package/dist/modules/catalog/validators/component-name-validator.js.map +1 -0
  147. package/dist/modules/catalog/validators/component-name-validator.test.d.ts +2 -0
  148. package/dist/modules/catalog/validators/component-name-validator.test.d.ts.map +1 -0
  149. package/dist/modules/catalog/validators/component-name-validator.test.js +196 -0
  150. package/dist/modules/catalog/validators/component-name-validator.test.js.map +1 -0
  151. package/dist/modules/deploy/commands/deploy-init-action.d.ts +12 -0
  152. package/dist/modules/deploy/commands/deploy-init-action.d.ts.map +1 -0
  153. package/dist/modules/deploy/commands/deploy-init-action.js +220 -0
  154. package/dist/modules/deploy/commands/deploy-init-action.js.map +1 -0
  155. package/dist/modules/deploy/commands/deploy-init.d.ts +6 -0
  156. package/dist/modules/deploy/commands/deploy-init.d.ts.map +1 -0
  157. package/dist/modules/deploy/commands/deploy-init.js +16 -0
  158. package/dist/modules/deploy/commands/deploy-init.js.map +1 -0
  159. package/dist/modules/deploy/commands/deploy-logs-action.d.ts +12 -0
  160. package/dist/modules/deploy/commands/deploy-logs-action.d.ts.map +1 -0
  161. package/dist/modules/deploy/commands/deploy-logs-action.js +113 -0
  162. package/dist/modules/deploy/commands/deploy-logs-action.js.map +1 -0
  163. package/dist/modules/deploy/commands/deploy-logs.d.ts +6 -0
  164. package/dist/modules/deploy/commands/deploy-logs.d.ts.map +1 -0
  165. package/dist/modules/deploy/commands/deploy-logs.js +17 -0
  166. package/dist/modules/deploy/commands/deploy-logs.js.map +1 -0
  167. package/dist/modules/deploy/commands/deploy-status-action.d.ts +8 -0
  168. package/dist/modules/deploy/commands/deploy-status-action.d.ts.map +1 -0
  169. package/dist/modules/deploy/commands/deploy-status-action.js +153 -0
  170. package/dist/modules/deploy/commands/deploy-status-action.js.map +1 -0
  171. package/dist/modules/deploy/commands/deploy-status.d.ts +6 -0
  172. package/dist/modules/deploy/commands/deploy-status.d.ts.map +1 -0
  173. package/dist/modules/deploy/commands/deploy-status.js +13 -0
  174. package/dist/modules/deploy/commands/deploy-status.js.map +1 -0
  175. package/dist/modules/deploy/index.d.ts +7 -0
  176. package/dist/modules/deploy/index.d.ts.map +1 -0
  177. package/dist/modules/deploy/index.js +7 -0
  178. package/dist/modules/deploy/index.js.map +1 -0
  179. package/dist/modules/deploy/services/deploy-svc.d.ts +17 -0
  180. package/dist/modules/deploy/services/deploy-svc.d.ts.map +1 -0
  181. package/dist/modules/deploy/services/deploy-svc.js +25 -0
  182. package/dist/modules/deploy/services/deploy-svc.js.map +1 -0
  183. package/dist/modules/deploy/utils/vercel-extended.d.ts +63 -0
  184. package/dist/modules/deploy/utils/vercel-extended.d.ts.map +1 -0
  185. package/dist/modules/deploy/utils/vercel-extended.js +238 -0
  186. package/dist/modules/deploy/utils/vercel-extended.js.map +1 -0
  187. package/dist/modules/plugin/commands/plugin-install.d.ts +3 -0
  188. package/dist/modules/plugin/commands/plugin-install.d.ts.map +1 -0
  189. package/dist/modules/plugin/commands/plugin-install.js +45 -0
  190. package/dist/modules/plugin/commands/plugin-install.js.map +1 -0
  191. package/dist/modules/plugin/errors/plugin-errors.d.ts +16 -0
  192. package/dist/modules/plugin/errors/plugin-errors.d.ts.map +1 -0
  193. package/dist/modules/plugin/errors/plugin-errors.js +39 -0
  194. package/dist/modules/plugin/errors/plugin-errors.js.map +1 -0
  195. package/dist/modules/plugin/index.d.ts +6 -0
  196. package/dist/modules/plugin/index.d.ts.map +1 -0
  197. package/dist/modules/plugin/index.js +9 -0
  198. package/dist/modules/plugin/index.js.map +1 -0
  199. package/dist/modules/plugin/lib/plugin-registry.d.ts +15 -0
  200. package/dist/modules/plugin/lib/plugin-registry.d.ts.map +1 -0
  201. package/dist/modules/plugin/lib/plugin-registry.js +47 -0
  202. package/dist/modules/plugin/lib/plugin-registry.js.map +1 -0
  203. package/dist/modules/plugin/services/plugin-svc.d.ts +10 -0
  204. package/dist/modules/plugin/services/plugin-svc.d.ts.map +1 -0
  205. package/dist/modules/plugin/services/plugin-svc.js +44 -0
  206. package/dist/modules/plugin/services/plugin-svc.js.map +1 -0
  207. package/dist/modules/plugin/types/plugin-types.d.ts +19 -0
  208. package/dist/modules/plugin/types/plugin-types.d.ts.map +1 -0
  209. package/dist/modules/plugin/types/plugin-types.js +2 -0
  210. package/dist/modules/plugin/types/plugin-types.js.map +1 -0
  211. package/dist/modules/startup/commands/create-startup.d.ts +3 -0
  212. package/dist/modules/startup/commands/create-startup.d.ts.map +1 -0
  213. package/dist/modules/startup/commands/create-startup.js +43 -0
  214. package/dist/modules/startup/commands/create-startup.js.map +1 -0
  215. package/dist/modules/startup/errors/startup-errors.d.ts +13 -0
  216. package/dist/modules/startup/errors/startup-errors.d.ts.map +1 -0
  217. package/dist/modules/startup/errors/startup-errors.js +25 -0
  218. package/dist/modules/startup/errors/startup-errors.js.map +1 -0
  219. package/dist/modules/startup/index.d.ts +5 -0
  220. package/dist/modules/startup/index.d.ts.map +1 -0
  221. package/dist/modules/startup/index.js +7 -0
  222. package/dist/modules/startup/index.js.map +1 -0
  223. package/dist/modules/startup/services/startup-service.d.ts +7 -0
  224. package/dist/modules/startup/services/startup-service.d.ts.map +1 -0
  225. package/dist/modules/startup/services/startup-service.js +43 -0
  226. package/dist/modules/startup/services/startup-service.js.map +1 -0
  227. package/dist/modules/startup/types/startup-types.d.ts +8 -0
  228. package/dist/modules/startup/types/startup-types.d.ts.map +1 -0
  229. package/dist/modules/startup/types/startup-types.js +2 -0
  230. package/dist/modules/startup/types/startup-types.js.map +1 -0
  231. package/dist/modules/startup/utils/startup-validators.d.ts +8 -0
  232. package/dist/modules/startup/utils/startup-validators.d.ts.map +1 -0
  233. package/dist/modules/startup/utils/startup-validators.js +17 -0
  234. package/dist/modules/startup/utils/startup-validators.js.map +1 -0
  235. package/dist/modules/workspace/commands/init-workspace.d.ts +3 -0
  236. package/dist/modules/workspace/commands/init-workspace.d.ts.map +1 -0
  237. package/dist/modules/workspace/commands/init-workspace.js +54 -0
  238. package/dist/modules/workspace/commands/init-workspace.js.map +1 -0
  239. package/dist/modules/workspace/errors/workspace-errors.d.ts +10 -0
  240. package/dist/modules/workspace/errors/workspace-errors.d.ts.map +1 -0
  241. package/dist/modules/workspace/errors/workspace-errors.js +19 -0
  242. package/dist/modules/workspace/errors/workspace-errors.js.map +1 -0
  243. package/dist/modules/workspace/index.d.ts +5 -0
  244. package/dist/modules/workspace/index.d.ts.map +1 -0
  245. package/dist/modules/workspace/index.js +7 -0
  246. package/dist/modules/workspace/index.js.map +1 -0
  247. package/dist/modules/workspace/services/workspace-service.d.ts +6 -0
  248. package/dist/modules/workspace/services/workspace-service.d.ts.map +1 -0
  249. package/dist/modules/workspace/services/workspace-service.js +46 -0
  250. package/dist/modules/workspace/services/workspace-service.js.map +1 -0
  251. package/dist/modules/workspace/types/workspace-types.d.ts +8 -0
  252. package/dist/modules/workspace/types/workspace-types.d.ts.map +1 -0
  253. package/dist/modules/workspace/types/workspace-types.js +2 -0
  254. package/dist/modules/workspace/types/workspace-types.js.map +1 -0
  255. package/dist/modules/workspace/utils/workspace-validators.d.ts +8 -0
  256. package/dist/modules/workspace/utils/workspace-validators.d.ts.map +1 -0
  257. package/dist/modules/workspace/utils/workspace-validators.js +17 -0
  258. package/dist/modules/workspace/utils/workspace-validators.js.map +1 -0
  259. package/dist/plugins/theme/package.json +32 -0
  260. package/dist/plugins/theme/plugin.json +9 -0
  261. package/dist/plugins/theme/src/generator.ts +92 -0
  262. package/dist/plugins/theme/src/utils/config-modifier.ts +142 -0
  263. package/dist/plugins/theme/src/utils/css-modifier.ts +89 -0
  264. package/dist/plugins/theme/templates/app/theme-test/page.tsx +156 -0
  265. package/dist/plugins/theme/templates/src/modules/theme/README.md +209 -0
  266. package/dist/plugins/theme/templates/src/modules/theme/config/brand.css +23 -0
  267. package/dist/plugins/theme/tsconfig.json +14 -0
  268. package/dist/plugins/theme/tsup.config.ts +10 -0
  269. package/dist/templates/startup/apps/.gitkeep +8 -0
  270. package/dist/templates/templates/startup/apps/.gitkeep +8 -0
  271. package/dist/templates/templates/workspace/.eslintignore +7 -0
  272. package/dist/templates/templates/workspace/.eslintrc.js +38 -0
  273. package/dist/templates/templates/workspace/.husky/pre-push +24 -0
  274. package/dist/templates/templates/workspace/.launch77/workspace.json +3 -0
  275. package/dist/templates/templates/workspace/.lintstagedrc.json +4 -0
  276. package/dist/templates/templates/workspace/.prettierrc +9 -0
  277. package/dist/templates/templates/workspace/README.md +62 -0
  278. package/dist/templates/templates/workspace/app-templates/.gitkeep +1 -0
  279. package/dist/templates/templates/workspace/apps/.gitkeep +1 -0
  280. package/dist/templates/templates/workspace/libraries/.gitkeep +1 -0
  281. package/dist/templates/templates/workspace/package.json +31 -0
  282. package/dist/templates/templates/workspace/plugins/.gitkeep +1 -0
  283. package/dist/templates/templates/workspace/tsconfig.json +22 -0
  284. package/dist/templates/templates/workspace/turbo.json +25 -0
  285. package/dist/templates/workspace/.launch77/workspace.json +3 -0
  286. package/dist/templates/workspace/README.md +62 -0
  287. package/dist/templates/workspace/app-templates/.gitkeep +1 -0
  288. package/dist/templates/workspace/apps/.gitkeep +1 -0
  289. package/dist/templates/workspace/libraries/.gitkeep +1 -0
  290. package/dist/templates/workspace/package.json +21 -0
  291. package/dist/templates/workspace/plugins/.gitkeep +1 -0
  292. package/dist/templates/workspace/tsconfig.json +22 -0
  293. package/dist/templates/workspace/turbo.json +25 -0
  294. package/dist/utils/launch77-context.d.ts +16 -0
  295. package/dist/utils/launch77-context.d.ts.map +1 -0
  296. package/dist/utils/launch77-context.js +111 -0
  297. package/dist/utils/launch77-context.js.map +1 -0
  298. package/dist/utils/launch77-validation.d.ts +17 -0
  299. package/dist/utils/launch77-validation.d.ts.map +1 -0
  300. package/dist/utils/launch77-validation.js +44 -0
  301. package/dist/utils/launch77-validation.js.map +1 -0
  302. package/dist/utils/monorepo.d.ts +19 -0
  303. package/dist/utils/monorepo.d.ts.map +1 -0
  304. package/dist/utils/monorepo.js +100 -0
  305. package/dist/utils/monorepo.js.map +1 -0
  306. package/dist/utils/validation.d.ts +32 -0
  307. package/dist/utils/validation.d.ts.map +1 -0
  308. package/dist/utils/validation.js +92 -0
  309. package/dist/utils/validation.js.map +1 -0
  310. package/dist/utils/version.d.ts +7 -0
  311. package/dist/utils/version.d.ts.map +1 -0
  312. package/dist/utils/version.js +16 -0
  313. package/dist/utils/version.js.map +1 -0
  314. package/launch77-cli-1.2.0.tgz +0 -0
  315. package/package.json +48 -0
  316. package/src/cli.ts +34 -0
  317. package/src/infrastructure/filesystem.ts +69 -0
  318. package/src/infrastructure/npm.ts +18 -0
  319. package/src/infrastructure/template-engine.ts +48 -0
  320. package/src/infrastructure/template-generator.ts +26 -0
  321. package/src/infrastructure/template.ts +66 -0
  322. package/src/modules/app/commands/create-app.ts +60 -0
  323. package/src/modules/app/commands/delete-app.ts +123 -0
  324. package/src/modules/app/commands/generate-manifest.ts +75 -0
  325. package/src/modules/app/commands/validate-manifest.ts +78 -0
  326. package/src/modules/app/errors/app-errors.ts +41 -0
  327. package/src/modules/app/index.ts +16 -0
  328. package/src/modules/app/lib/manifest-schema.ts +65 -0
  329. package/src/modules/app/services/app-svc.ts +115 -0
  330. package/src/modules/app/services/manifest-svc.ts +144 -0
  331. package/src/modules/app/types/app-types.ts +27 -0
  332. package/src/modules/catalog/commands/generate.ts +115 -0
  333. package/src/modules/catalog/commands/scan.ts +48 -0
  334. package/src/modules/catalog/config/catalog-config.test.ts +404 -0
  335. package/src/modules/catalog/config/catalog-config.ts +130 -0
  336. package/src/modules/catalog/index.ts +9 -0
  337. package/src/modules/catalog/parsers/typescript-parser.ts +93 -0
  338. package/src/modules/catalog/scanners/metadata-extractor.ts +134 -0
  339. package/src/modules/catalog/scanners/metadata-validator.ts +67 -0
  340. package/src/modules/catalog/scanners/ui-component-scanner.ts +185 -0
  341. package/src/modules/catalog/schemas/catalog-ui-components.schema.json +145 -0
  342. package/src/modules/catalog/services/catalog-generator-svc.test.ts +233 -0
  343. package/src/modules/catalog/services/catalog-generator-svc.ts +124 -0
  344. package/src/modules/catalog/services/catalog-svc.ts +222 -0
  345. package/src/modules/catalog/types/catalog-types.ts +149 -0
  346. package/src/modules/catalog/utils/examples-generator.ts +68 -0
  347. package/src/modules/catalog/utils/quality-reporter.ts +76 -0
  348. package/src/modules/catalog/validators/component-name-validator.test.ts +233 -0
  349. package/src/modules/catalog/validators/component-name-validator.ts +43 -0
  350. package/src/modules/deploy/commands/deploy-init-action.ts +247 -0
  351. package/src/modules/deploy/commands/deploy-init.ts +19 -0
  352. package/src/modules/deploy/commands/deploy-logs-action.ts +135 -0
  353. package/src/modules/deploy/commands/deploy-logs.ts +20 -0
  354. package/src/modules/deploy/commands/deploy-status-action.ts +175 -0
  355. package/src/modules/deploy/commands/deploy-status.ts +16 -0
  356. package/src/modules/deploy/index.ts +7 -0
  357. package/src/modules/deploy/services/deploy-svc.ts +31 -0
  358. package/src/modules/deploy/utils/vercel-extended.ts +286 -0
  359. package/src/modules/plugin/commands/plugin-install.ts +51 -0
  360. package/src/modules/plugin/errors/plugin-errors.ts +44 -0
  361. package/src/modules/plugin/index.ts +14 -0
  362. package/src/modules/plugin/lib/launch77-workspace.code-workspace +14 -0
  363. package/src/modules/plugin/lib/plugin-registry.ts +59 -0
  364. package/src/modules/plugin/services/plugin-svc.ts +53 -0
  365. package/src/modules/plugin/types/plugin-types.ts +21 -0
  366. package/src/modules/startup/commands/create-startup.ts +53 -0
  367. package/src/modules/startup/errors/startup-errors.ts +23 -0
  368. package/src/modules/startup/index.ts +11 -0
  369. package/src/modules/startup/services/startup-service.ts +57 -0
  370. package/src/modules/startup/types/startup-types.ts +8 -0
  371. package/src/modules/startup/utils/startup-validators.ts +19 -0
  372. package/src/modules/workspace/commands/init-workspace.ts +63 -0
  373. package/src/modules/workspace/errors/workspace-errors.ts +16 -0
  374. package/src/modules/workspace/index.ts +11 -0
  375. package/src/modules/workspace/services/workspace-service.ts +59 -0
  376. package/src/modules/workspace/types/workspace-types.ts +8 -0
  377. package/src/modules/workspace/utils/workspace-validators.ts +19 -0
  378. package/src/utils/launch77-context.ts +152 -0
  379. package/src/utils/launch77-validation.ts +59 -0
  380. package/src/utils/monorepo.ts +137 -0
  381. package/src/utils/validation.ts +117 -0
  382. package/src/utils/version.ts +16 -0
  383. package/templates/startup/apps/.gitkeep +8 -0
  384. package/templates/workspace/.eslintignore +7 -0
  385. package/templates/workspace/.eslintrc.js +38 -0
  386. package/templates/workspace/.husky/pre-push +24 -0
  387. package/templates/workspace/.launch77/workspace.json +3 -0
  388. package/templates/workspace/.lintstagedrc.json +4 -0
  389. package/templates/workspace/.prettierrc +9 -0
  390. package/templates/workspace/README.md +62 -0
  391. package/templates/workspace/app-templates/.gitkeep +1 -0
  392. package/templates/workspace/apps/.gitkeep +1 -0
  393. package/templates/workspace/libraries/.gitkeep +1 -0
  394. package/templates/workspace/package.json +31 -0
  395. package/templates/workspace/plugins/.gitkeep +1 -0
  396. package/templates/workspace/tsconfig.json +22 -0
  397. package/templates/workspace/turbo.json +25 -0
  398. package/tsconfig.json +23 -0
@@ -0,0 +1,8 @@
1
+ export interface CreateStartupRequest {
2
+ name: string
3
+ }
4
+
5
+ export interface CreateStartupResult {
6
+ startupPath: string
7
+ name: string
8
+ }
@@ -0,0 +1,19 @@
1
+ import { InvalidStartupNameError } from '../errors/startup-errors.js'
2
+
3
+ /**
4
+ * Validate startup name format
5
+ * - Must be lowercase
6
+ * - Can only contain lowercase letters, numbers, and hyphens
7
+ * - Cannot contain spaces, uppercase, underscores, periods, or special characters
8
+ */
9
+ export function validateStartupName(name: string): void {
10
+ if (!name || name.trim().length === 0) {
11
+ throw new Error('Startup name cannot be empty')
12
+ }
13
+
14
+ const validPattern = /^[a-z0-9-]+$/
15
+
16
+ if (!validPattern.test(name)) {
17
+ throw new InvalidStartupNameError(name)
18
+ }
19
+ }
@@ -0,0 +1,63 @@
1
+ import * as path from 'path'
2
+
3
+ import { input } from '@inquirer/prompts'
4
+ import chalk from 'chalk'
5
+ import { Command } from 'commander'
6
+ import ora from 'ora'
7
+
8
+ import { WorkspaceAlreadyExistsError, InvalidWorkspaceNameError } from '../errors/workspace-errors.js'
9
+ import { WorkspaceService } from '../services/workspace-service.js'
10
+
11
+ export function initWorkspaceCommand(): Command {
12
+ return new Command('init')
13
+ .argument('[name]', 'Name of the workspace (lowercase, alphanumeric, hyphens)')
14
+ .description('Initialize a new Launch77 workspace')
15
+ .action(async (name?: string) => {
16
+ // If no name provided, prompt for it
17
+ let workspaceName = name
18
+ if (!workspaceName) {
19
+ workspaceName = await input({
20
+ message: 'Workspace name:',
21
+ validate: (value) => {
22
+ if (!value || value.trim().length === 0) {
23
+ return 'Workspace name cannot be empty'
24
+ }
25
+ const validPattern = /^[a-z0-9-]+$/
26
+ if (!validPattern.test(value)) {
27
+ return 'Workspace name must be lowercase, alphanumeric, and hyphens only'
28
+ }
29
+ return true
30
+ },
31
+ })
32
+ }
33
+
34
+ console.log(chalk.blue(`\n🚀 Initializing workspace: ${workspaceName}\n`))
35
+
36
+ const service = new WorkspaceService()
37
+
38
+ try {
39
+ const spinner = ora('Creating workspace structure...').start()
40
+
41
+ const result = await service.initWorkspace({ name: workspaceName }, process.cwd())
42
+
43
+ spinner.succeed('Workspace structure created')
44
+
45
+ // Success message
46
+ const cwd = process.cwd()
47
+ console.log(chalk.green(`\n✅ Workspace created successfully at ${path.relative(cwd, result.workspacePath)}\n`))
48
+ console.log(chalk.gray(`Next steps:\n` + ` 1. cd ${workspaceName}\n` + ` 2. npm install\n` + ` 3. Create your first app with launch77 commands\n`))
49
+ } catch (error) {
50
+ if (error instanceof WorkspaceAlreadyExistsError) {
51
+ console.error(chalk.red(`\n❌ ${error.message}\n`))
52
+ process.exit(1)
53
+ }
54
+
55
+ if (error instanceof InvalidWorkspaceNameError) {
56
+ console.error(chalk.red(`\n❌ ${error.message}\n`))
57
+ process.exit(1)
58
+ }
59
+
60
+ throw error
61
+ }
62
+ })
63
+ }
@@ -0,0 +1,16 @@
1
+ export class WorkspaceAlreadyExistsError extends Error {
2
+ constructor(
3
+ public workspaceName: string,
4
+ public workspacePath: string
5
+ ) {
6
+ super(`Workspace ${workspaceName} already exists at ${workspacePath}`)
7
+ this.name = 'WorkspaceAlreadyExistsError'
8
+ }
9
+ }
10
+
11
+ export class InvalidWorkspaceNameError extends Error {
12
+ constructor(public workspaceName: string) {
13
+ super(`Invalid workspace name: "${workspaceName}"\n\n` + `Workspace names must:\n` + ` • Be lowercase only\n` + ` • Can only contain: lowercase letters, numbers, and hyphens\n` + ` • Cannot contain: spaces, uppercase, underscores, periods, or special characters\n\n` + `Valid examples:\n` + ` ✓ my-workspace\n` + ` ✓ myworkspace\n` + ` ✓ workspace123\n\n` + `Invalid examples:\n` + ` ✗ MyWorkspace (uppercase)\n` + ` ✗ my_workspace (underscores)\n` + ` ✗ my.workspace (periods)\n` + ` ✗ my workspace (spaces)`)
14
+ this.name = 'InvalidWorkspaceNameError'
15
+ }
16
+ }
@@ -0,0 +1,11 @@
1
+ // Services
2
+ export { WorkspaceService } from './services/workspace-service.js'
3
+
4
+ // Types
5
+ export type { InitWorkspaceRequest, InitWorkspaceResult } from './types/workspace-types.js'
6
+
7
+ // Errors
8
+ export { WorkspaceAlreadyExistsError, InvalidWorkspaceNameError } from './errors/workspace-errors.js'
9
+
10
+ // Command
11
+ export { initWorkspaceCommand } from './commands/init-workspace.js'
@@ -0,0 +1,59 @@
1
+ import * as path from 'path'
2
+ import { fileURLToPath } from 'url'
3
+
4
+ import * as filesystem from '../../../infrastructure/filesystem.js'
5
+ import { WorkspaceAlreadyExistsError } from '../errors/workspace-errors.js'
6
+ import { validateWorkspaceName } from '../utils/workspace-validators.js'
7
+
8
+ import type { InitWorkspaceRequest, InitWorkspaceResult } from '../types/workspace-types.js'
9
+
10
+ export class WorkspaceService {
11
+ async initWorkspace(request: InitWorkspaceRequest, cwd: string): Promise<InitWorkspaceResult> {
12
+ const { name } = request
13
+
14
+ // 1. Validate name
15
+ validateWorkspaceName(name)
16
+
17
+ // 2. Determine workspace path (relative to current directory)
18
+ const workspacePath = path.join(cwd, name)
19
+
20
+ // 3. Check if workspace already exists
21
+ if (await filesystem.exists(workspacePath)) {
22
+ throw new WorkspaceAlreadyExistsError(name, workspacePath)
23
+ }
24
+
25
+ // 4. Copy template
26
+ const templatePath = this.getTemplatePath()
27
+ await filesystem.copy(templatePath, workspacePath)
28
+
29
+ // 5. Update package.json with workspace name
30
+ const packageJsonPath = path.join(workspacePath, 'package.json')
31
+ const packageJson = await filesystem.readJSON<{ name?: string }>(packageJsonPath)
32
+ packageJson.name = name
33
+ await filesystem.writeJSON(packageJsonPath, packageJson)
34
+
35
+ // 6. Verify workspace.json was created
36
+ const workspaceJsonPath = path.join(workspacePath, '.launch77', 'workspace.json')
37
+ if (!(await filesystem.exists(workspaceJsonPath))) {
38
+ throw new Error('Failed to create workspace manifest (.launch77/workspace.json)')
39
+ }
40
+
41
+ return {
42
+ workspacePath,
43
+ name,
44
+ }
45
+ }
46
+
47
+ private getTemplatePath(): string {
48
+ const __filename = fileURLToPath(import.meta.url)
49
+ const __dirname = path.dirname(__filename)
50
+
51
+ // From src/modules/workspace/services/ or dist/modules/workspace/services/ up to cli root
52
+ const isDev = __filename.includes('/src/')
53
+ const cliRoot = isDev
54
+ ? path.join(__dirname, '../../../..') // src/modules/workspace/services -> cli root
55
+ : path.join(__dirname, '../../../..') // dist/modules/workspace/services -> cli root
56
+
57
+ return path.join(cliRoot, 'templates/workspace')
58
+ }
59
+ }
@@ -0,0 +1,8 @@
1
+ export interface InitWorkspaceRequest {
2
+ name: string
3
+ }
4
+
5
+ export interface InitWorkspaceResult {
6
+ workspacePath: string
7
+ name: string
8
+ }
@@ -0,0 +1,19 @@
1
+ import { InvalidWorkspaceNameError } from '../errors/workspace-errors.js'
2
+
3
+ /**
4
+ * Validate workspace name format
5
+ * - Must be lowercase
6
+ * - Can only contain lowercase letters, numbers, and hyphens
7
+ * - Cannot contain spaces, uppercase, underscores, periods, or special characters
8
+ */
9
+ export function validateWorkspaceName(name: string): void {
10
+ if (!name || name.trim().length === 0) {
11
+ throw new Error('Workspace name cannot be empty')
12
+ }
13
+
14
+ const validPattern = /^[a-z0-9-]+$/
15
+
16
+ if (!validPattern.test(name)) {
17
+ throw new InvalidWorkspaceNameError(name)
18
+ }
19
+ }
@@ -0,0 +1,152 @@
1
+ import * as path from 'path'
2
+
3
+ import fs from 'fs-extra'
4
+
5
+ export type Launch77LocationType = 'workspace-root' | 'workspace-app' | 'unknown'
6
+
7
+ export interface Launch77Context {
8
+ isValid: boolean
9
+ locationType: Launch77LocationType
10
+ workspaceRoot: string
11
+ appsDir: string
12
+ workspaceVersion: string
13
+ workspaceName: string
14
+ appName?: string
15
+ packageName: string
16
+ }
17
+
18
+ const WORKSPACE_MANIFEST = '.launch77/workspace.json'
19
+
20
+ interface WorkspaceManifest {
21
+ version: string
22
+ }
23
+
24
+ /**
25
+ * Check if a directory contains a Launch77 workspace manifest
26
+ */
27
+ async function isWorkspaceRoot(dir: string): Promise<boolean> {
28
+ const manifestPath = path.join(dir, WORKSPACE_MANIFEST)
29
+ return await fs.pathExists(manifestPath)
30
+ }
31
+
32
+ /**
33
+ * Read workspace manifest
34
+ */
35
+ async function readWorkspaceManifest(workspaceRoot: string): Promise<WorkspaceManifest> {
36
+ const manifestPath = path.join(workspaceRoot, WORKSPACE_MANIFEST)
37
+ return await fs.readJSON(manifestPath)
38
+ }
39
+
40
+ /**
41
+ * Find the workspace root by traversing up from a starting directory
42
+ * Looks for .launch77/workspace.json manifest file
43
+ */
44
+ async function findWorkspaceRoot(startDir: string): Promise<string | null> {
45
+ let currentDir = path.resolve(startDir)
46
+ const rootDir = path.parse(currentDir).root
47
+
48
+ while (currentDir !== rootDir) {
49
+ if (await isWorkspaceRoot(currentDir)) {
50
+ return currentDir
51
+ }
52
+ currentDir = path.dirname(currentDir)
53
+ }
54
+
55
+ return null
56
+ }
57
+
58
+ interface ParsedLocation {
59
+ locationType: Launch77LocationType
60
+ appName?: string
61
+ }
62
+
63
+ /**
64
+ * Parse the directory structure to determine location context
65
+ * Based on patterns:
66
+ * - apps/[name] → workspace-app
67
+ * - (empty or root) → workspace-root
68
+ */
69
+ function parseLocationFromPath(cwdPath: string, workspaceRoot: string): ParsedLocation {
70
+ const relativePath = path.relative(workspaceRoot, cwdPath)
71
+
72
+ // At workspace root
73
+ if (!relativePath || relativePath === '.') {
74
+ return { locationType: 'workspace-root' }
75
+ }
76
+
77
+ const parts = relativePath.split(path.sep)
78
+
79
+ // apps/[app-name]/...
80
+ if (parts[0] === 'apps' && parts.length >= 2) {
81
+ return {
82
+ locationType: 'workspace-app',
83
+ appName: parts[1],
84
+ }
85
+ }
86
+
87
+ // Somewhere else in workspace (libraries, plugins, etc.)
88
+ // Still considered workspace-root for now
89
+ return { locationType: 'workspace-root' }
90
+ }
91
+
92
+ /**
93
+ * Detect the Launch77 workspace context from the current working directory
94
+ */
95
+ export async function detectLaunch77Context(cwd: string): Promise<Launch77Context> {
96
+ const resolvedCwd = path.resolve(cwd)
97
+
98
+ // Find the workspace root
99
+ const workspaceRoot = await findWorkspaceRoot(resolvedCwd)
100
+
101
+ if (!workspaceRoot) {
102
+ return {
103
+ isValid: false,
104
+ locationType: 'unknown',
105
+ workspaceRoot: '',
106
+ appsDir: '',
107
+ workspaceVersion: '',
108
+ workspaceName: '',
109
+ packageName: '',
110
+ }
111
+ }
112
+
113
+ // Read workspace manifest
114
+ let manifest: WorkspaceManifest
115
+ try {
116
+ manifest = await readWorkspaceManifest(workspaceRoot)
117
+ } catch (error) {
118
+ // Manifest exists (we found it) but couldn't read it
119
+ return {
120
+ isValid: false,
121
+ locationType: 'unknown',
122
+ workspaceRoot: '',
123
+ appsDir: '',
124
+ workspaceVersion: '',
125
+ workspaceName: '',
126
+ packageName: '',
127
+ }
128
+ }
129
+
130
+ // Parse the directory structure to determine location
131
+ const parsed = parseLocationFromPath(resolvedCwd, workspaceRoot)
132
+
133
+ // Workspace name is the directory name
134
+ const workspaceName = path.basename(workspaceRoot)
135
+
136
+ // Apps directory is always {workspaceRoot}/apps
137
+ const appsDir = path.join(workspaceRoot, 'apps')
138
+
139
+ // Package name: @{workspaceName}/{appName} (if in app)
140
+ const packageName = parsed.appName ? `@${workspaceName}/${parsed.appName}` : ''
141
+
142
+ return {
143
+ isValid: true,
144
+ locationType: parsed.locationType,
145
+ workspaceRoot,
146
+ appsDir,
147
+ workspaceVersion: manifest.version,
148
+ workspaceName,
149
+ appName: parsed.appName,
150
+ packageName,
151
+ }
152
+ }
@@ -0,0 +1,59 @@
1
+ import type { Launch77Context } from './launch77-context.js'
2
+
3
+ /**
4
+ * Validation result with helpful error messages
5
+ */
6
+ export interface ValidationResult {
7
+ valid: boolean
8
+ errorMessage?: string
9
+ }
10
+
11
+ /**
12
+ * Validate that we're in a workspace context for app:create
13
+ */
14
+ export function validateWorkspaceContext(context: Launch77Context): ValidationResult {
15
+ if (!context.isValid) {
16
+ return {
17
+ valid: false,
18
+ errorMessage: 'Must be run from within a Launch77 workspace.\n\n' + 'Create a workspace first:\n' + ' launch77 init my-workspace\n' + ' cd my-workspace',
19
+ }
20
+ }
21
+
22
+ if (context.locationType !== 'workspace-root' && context.locationType !== 'workspace-app') {
23
+ return {
24
+ valid: false,
25
+ errorMessage: 'Must be run from workspace root or app directory',
26
+ }
27
+ }
28
+
29
+ return { valid: true }
30
+ }
31
+
32
+ /**
33
+ * Validate that we're inside an app directory (for plugin:install - future use)
34
+ */
35
+ export function validateAppContext(context: Launch77Context): ValidationResult {
36
+ if (!context.isValid) {
37
+ return {
38
+ valid: false,
39
+ errorMessage: 'Must be run from within a Launch77 workspace app directory',
40
+ }
41
+ }
42
+
43
+ // Must be inside an app, not just workspace root
44
+ if (context.locationType !== 'workspace-app') {
45
+ return {
46
+ valid: false,
47
+ errorMessage: 'plugin:install must be run from within an app directory.\n\n' + 'Navigate to an app:\n' + ' cd apps/<app-name>',
48
+ }
49
+ }
50
+
51
+ if (!context.appName) {
52
+ return {
53
+ valid: false,
54
+ errorMessage: 'Could not determine app name from current directory.\nThis is a bug. Please report it.',
55
+ }
56
+ }
57
+
58
+ return { valid: true }
59
+ }
@@ -0,0 +1,137 @@
1
+ import * as path from 'path'
2
+
3
+ import fs from 'fs-extra'
4
+
5
+ export type LocationType = 'monorepo-root' | 'startup-root' | 'startup-app' | 'unknown'
6
+
7
+ export interface MonorepoContext {
8
+ isValid: boolean
9
+ location: LocationType
10
+ monorepoRoot: string
11
+ appsDir: string | null
12
+ // Specific context info
13
+ startupName?: string
14
+ appName?: string
15
+ packageName?: string
16
+ }
17
+
18
+ const MONOREPO_ROOT_MARKER = '.launch-monorepo-root.md'
19
+
20
+ /**
21
+ * Check if a directory is the monorepo root by looking for the marker file
22
+ */
23
+ async function isMonorepoRoot(dir: string): Promise<boolean> {
24
+ const markerPath = path.join(dir, MONOREPO_ROOT_MARKER)
25
+ return await fs.pathExists(markerPath)
26
+ }
27
+
28
+ /**
29
+ * Find the monorepo root by traversing up from a starting directory
30
+ * Looks for .launch-monorepo-root.md marker file
31
+ */
32
+ async function findMonorepoRoot(startDir: string): Promise<string | null> {
33
+ let currentDir = path.resolve(startDir)
34
+ const rootDir = path.parse(currentDir).root
35
+
36
+ while (currentDir !== rootDir) {
37
+ if (await isMonorepoRoot(currentDir)) {
38
+ return currentDir
39
+ }
40
+ currentDir = path.dirname(currentDir)
41
+ }
42
+
43
+ return null
44
+ }
45
+
46
+ interface ParsedLocation {
47
+ location: LocationType
48
+ startupName?: string
49
+ appName?: string
50
+ }
51
+
52
+ /**
53
+ * Parse the directory structure to determine location context
54
+ * Based on patterns:
55
+ * - startups/[name] → startup-root
56
+ * - startups/[name]/apps/[app] → startup-app
57
+ * - (empty or root) → monorepo-root
58
+ */
59
+ function parseLocationFromPath(cwdPath: string, monorepoRoot: string): ParsedLocation {
60
+ const relativePath = path.relative(monorepoRoot, cwdPath)
61
+
62
+ // At monorepo root
63
+ if (!relativePath || relativePath === '.') {
64
+ return { location: 'monorepo-root' }
65
+ }
66
+
67
+ const parts = relativePath.split(path.sep)
68
+
69
+ // startups/[name]/...
70
+ if (parts[0] === 'startups' && parts.length >= 2) {
71
+ const startupName = parts[1]
72
+
73
+ // startups/[name]/apps/[app]
74
+ if (parts.length >= 4 && parts[2] === 'apps') {
75
+ return {
76
+ location: 'startup-app',
77
+ startupName,
78
+ appName: parts[3],
79
+ }
80
+ }
81
+
82
+ // startups/[name]
83
+ return {
84
+ location: 'startup-root',
85
+ startupName,
86
+ }
87
+ }
88
+
89
+ return { location: 'unknown' }
90
+ }
91
+
92
+ /**
93
+ * Determine the apps directory based on location context
94
+ * Returns null for invalid locations (e.g., monorepo-root, unknown)
95
+ */
96
+ function getAppsDir(monorepoRoot: string, location: LocationType, startupName?: string): string | null {
97
+ if (location === 'startup-root' || location === 'startup-app') {
98
+ return path.join(monorepoRoot, 'startups', startupName!, 'apps')
99
+ }
100
+ return null
101
+ }
102
+
103
+ /**
104
+ * Detect the monorepo context from the current working directory
105
+ *
106
+ * @deprecated Use detectLaunch77Context() from launch77-context.ts instead.
107
+ * This function is maintained for legacy monorepo structure only.
108
+ * New workspaces created with `launch77 init` should use the new context system.
109
+ */
110
+ export async function detectMonorepoContext(cwd: string): Promise<MonorepoContext> {
111
+ const resolvedCwd = path.resolve(cwd)
112
+
113
+ // Find the monorepo root
114
+ const monorepoRoot = await findMonorepoRoot(resolvedCwd)
115
+
116
+ if (!monorepoRoot) {
117
+ return {
118
+ isValid: false,
119
+ location: 'unknown',
120
+ monorepoRoot: '',
121
+ appsDir: null,
122
+ }
123
+ }
124
+
125
+ // Parse the directory structure to determine location
126
+ const parsed = parseLocationFromPath(resolvedCwd, monorepoRoot)
127
+ const appsDir = getAppsDir(monorepoRoot, parsed.location, parsed.startupName)
128
+
129
+ return {
130
+ isValid: true,
131
+ location: parsed.location,
132
+ monorepoRoot,
133
+ appsDir,
134
+ startupName: parsed.startupName,
135
+ appName: parsed.appName,
136
+ }
137
+ }
@@ -0,0 +1,117 @@
1
+ import * as path from 'path'
2
+
3
+ import chalk from 'chalk'
4
+
5
+ import type { MonorepoContext } from './monorepo.js'
6
+
7
+ /**
8
+ * Validation result with helpful error messages
9
+ */
10
+ export interface ValidationResult {
11
+ valid: boolean
12
+ errorMessage?: string
13
+ }
14
+
15
+ /**
16
+ * Validate that we're in a startup context (for create-app)
17
+ */
18
+ export function validateStartupContext(context: MonorepoContext): ValidationResult {
19
+ if (!context.isValid) {
20
+ return {
21
+ valid: false,
22
+ errorMessage: 'Must be run from within a Launch77 monorepo (could not find .launch-monorepo-root.md)',
23
+ }
24
+ }
25
+
26
+ if (context.location !== 'startup-root' && context.location !== 'startup-app') {
27
+ return {
28
+ valid: false,
29
+ errorMessage: `Command must be run from a startup directory.\n\n` + `${chalk.gray('Current location:')} ${context.location}\n` + `${chalk.gray('Expected:')} Within startups/<startup-name>/\n\n` + `${chalk.yellow('Navigate to a startup first:')}\n` + ` cd startups/<startup-name>/\n\n` + `${chalk.yellow('Or create a startup:')}\n` + ` launch77 create-startup <startup-name>`,
30
+ }
31
+ }
32
+
33
+ if (!context.appsDir) {
34
+ return {
35
+ valid: false,
36
+ errorMessage: `Could not determine apps directory for location: ${context.location}\n` + `This is a bug. Please report it.`,
37
+ }
38
+ }
39
+
40
+ return { valid: true }
41
+ }
42
+
43
+ /**
44
+ * Validate that we're inside an app directory (for plugin:install)
45
+ */
46
+ export function validateAppContext(context: MonorepoContext): ValidationResult {
47
+ if (!context.isValid) {
48
+ return {
49
+ valid: false,
50
+ errorMessage: 'Must be run from within a Launch77 monorepo (could not find .launch-monorepo-root.md)',
51
+ }
52
+ }
53
+
54
+ // Must be inside an app, not just a startup
55
+ if (context.location !== 'startup-app') {
56
+ return {
57
+ valid: false,
58
+ errorMessage: `plugin:install must be run from within an app directory.\n\n` + `${chalk.gray('Current location:')} ${context.location}\n` + `${chalk.gray('Expected:')} startups/<startup-name>/apps/<app-name>/\n\n` + `${chalk.yellow('Navigate to an app directory:')}\n` + ` cd startups/<startup-name>/apps/<app-name>/\n\n` + `${chalk.yellow('Or create an app first:')}\n` + ` cd startups/<startup-name>/\n` + ` launch77 app:create api <app-name>`,
59
+ }
60
+ }
61
+
62
+ if (!context.appName) {
63
+ return {
64
+ valid: false,
65
+ errorMessage: `Could not determine app name from current directory.\n` + `This is a bug. Please report it.`,
66
+ }
67
+ }
68
+
69
+ return { valid: true }
70
+ }
71
+
72
+ /**
73
+ * Get the app directory path
74
+ * Returns null if not in an app context
75
+ */
76
+ export function getAppDirectory(context: MonorepoContext): string | null {
77
+ if (context.location !== 'startup-app') {
78
+ return null
79
+ }
80
+
81
+ if (!context.monorepoRoot || !context.startupName || !context.appName) {
82
+ return null
83
+ }
84
+
85
+ return path.join(context.monorepoRoot, 'startups', context.startupName, 'apps', context.appName)
86
+ }
87
+
88
+ /**
89
+ * Validate app name format
90
+ *
91
+ * App names must follow Launch77 naming conventions:
92
+ * - Must be lowercase
93
+ * - Must start with a letter [a-z]
94
+ * - Can only contain lowercase letters, numbers, and hyphens
95
+ * - Cannot contain spaces, uppercase, underscores, periods, or special characters
96
+ */
97
+ export function validateAppName(appName: string): ValidationResult {
98
+ // Check for empty name
99
+ if (!appName || appName.trim().length === 0) {
100
+ return {
101
+ valid: false,
102
+ errorMessage: 'App name cannot be empty',
103
+ }
104
+ }
105
+
106
+ // Launch77 naming convention: lowercase, letters, numbers, hyphens only
107
+ const validPattern = /^[a-z][a-z0-9-]*$/
108
+
109
+ if (!validPattern.test(appName)) {
110
+ return {
111
+ valid: false,
112
+ errorMessage: `Invalid app name: "${appName}"\n\n` + `${chalk.gray('App names must:')}\n` + ` • Be lowercase only\n` + ` • Start with a letter\n` + ` • Can only contain: lowercase letters, numbers, and hyphens\n` + ` • Cannot contain: spaces, uppercase, underscores, periods, or special characters\n\n` + `${chalk.yellow('Valid examples:')}\n` + ` ✓ my-app\n` + ` ✓ myapp\n` + ` ✓ myapp123\n` + ` ✓ app-123\n\n` + `${chalk.red('Invalid examples:')}\n` + ` ✗ MyApp (uppercase)\n` + ` ✗ my_app (underscores)\n` + ` ✗ my.app (periods)\n` + ` ✗ my app (spaces)\n` + ` ✗ @myapp (special characters)\n` + ` ✗ -myapp (starts with hyphen)\n` + ` ✗ 123app (starts with number)`,
113
+ }
114
+ }
115
+
116
+ return { valid: true }
117
+ }