@launch77/cli 1.4.4 → 1.6.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 (195) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/cli.js +16 -17
  3. package/dist/cli.js.map +1 -1
  4. package/dist/infrastructure/package-resolver.d.ts +12 -2
  5. package/dist/infrastructure/package-resolver.d.ts.map +1 -1
  6. package/dist/infrastructure/package-resolver.js +29 -2
  7. package/dist/infrastructure/package-resolver.js.map +1 -1
  8. package/dist/infrastructure/package-resolver.test.js +3 -3
  9. package/dist/infrastructure/package-resolver.test.js.map +1 -1
  10. package/dist/infrastructure/template.d.ts +0 -1
  11. package/dist/infrastructure/template.d.ts.map +1 -1
  12. package/dist/infrastructure/template.js.map +1 -1
  13. package/dist/modules/app/commands/create-app.d.ts +1 -1
  14. package/dist/modules/app/commands/create-app.d.ts.map +1 -1
  15. package/dist/modules/app/commands/create-app.js +9 -14
  16. package/dist/modules/app/commands/create-app.js.map +1 -1
  17. package/dist/modules/app/commands/delete-app.d.ts +1 -1
  18. package/dist/modules/app/commands/delete-app.js +2 -2
  19. package/dist/modules/app/commands/delete-app.js.map +1 -1
  20. package/dist/modules/app/index.d.ts +3 -5
  21. package/dist/modules/app/index.d.ts.map +1 -1
  22. package/dist/modules/app/index.js +2 -4
  23. package/dist/modules/app/index.js.map +1 -1
  24. package/dist/modules/app/lib/app-template-resolver.d.ts.map +1 -1
  25. package/dist/modules/app/lib/app-template-resolver.js +15 -0
  26. package/dist/modules/app/lib/app-template-resolver.js.map +1 -1
  27. package/dist/modules/app/lib/manifest-schema.d.ts +0 -7
  28. package/dist/modules/app/lib/manifest-schema.d.ts.map +1 -1
  29. package/dist/modules/app/lib/manifest-schema.js +0 -2
  30. package/dist/modules/app/lib/manifest-schema.js.map +1 -1
  31. package/dist/modules/app/services/app-svc.d.ts.map +1 -1
  32. package/dist/modules/app/services/app-svc.js +36 -6
  33. package/dist/modules/app/services/app-svc.js.map +1 -1
  34. package/dist/modules/app/services/manifest-svc.d.ts +2 -2
  35. package/dist/modules/app/services/manifest-svc.d.ts.map +1 -1
  36. package/dist/modules/app/services/manifest-svc.js +9 -50
  37. package/dist/modules/app/services/manifest-svc.js.map +1 -1
  38. package/dist/modules/app/types/app-types.d.ts +2 -5
  39. package/dist/modules/app/types/app-types.d.ts.map +1 -1
  40. package/dist/modules/app/types/app-types.js +1 -4
  41. package/dist/modules/app/types/app-types.js.map +1 -1
  42. package/dist/modules/catalog/commands/generate.d.ts +1 -1
  43. package/dist/modules/catalog/commands/generate.d.ts.map +1 -1
  44. package/dist/modules/catalog/commands/generate.js +1 -1
  45. package/dist/modules/catalog/commands/generate.js.map +1 -1
  46. package/dist/modules/catalog/commands/scan.d.ts +1 -1
  47. package/dist/modules/catalog/commands/scan.d.ts.map +1 -1
  48. package/dist/modules/catalog/commands/scan.js +4 -4
  49. package/dist/modules/catalog/commands/scan.js.map +1 -1
  50. package/dist/modules/catalog/services/catalog-svc.d.ts +2 -2
  51. package/dist/modules/catalog/services/catalog-svc.d.ts.map +1 -1
  52. package/dist/modules/catalog/services/catalog-svc.js +2 -2
  53. package/dist/modules/catalog/services/catalog-svc.js.map +1 -1
  54. package/dist/modules/deploy/commands/deploy-init-action.d.ts.map +1 -1
  55. package/dist/modules/deploy/commands/deploy-init-action.js +21 -20
  56. package/dist/modules/deploy/commands/deploy-init-action.js.map +1 -1
  57. package/dist/modules/deploy/commands/deploy-logs-action.d.ts.map +1 -1
  58. package/dist/modules/deploy/commands/deploy-logs-action.js +16 -13
  59. package/dist/modules/deploy/commands/deploy-logs-action.js.map +1 -1
  60. package/dist/modules/deploy/commands/deploy-status-action.d.ts.map +1 -1
  61. package/dist/modules/deploy/commands/deploy-status-action.js +16 -13
  62. package/dist/modules/deploy/commands/deploy-status-action.js.map +1 -1
  63. package/dist/modules/library/commands/delete-library.d.ts +3 -0
  64. package/dist/modules/library/commands/delete-library.d.ts.map +1 -0
  65. package/dist/modules/library/commands/delete-library.js +111 -0
  66. package/dist/modules/library/commands/delete-library.js.map +1 -0
  67. package/dist/modules/library/errors/library-errors.d.ts +7 -0
  68. package/dist/modules/library/errors/library-errors.d.ts.map +1 -0
  69. package/dist/modules/library/errors/library-errors.js +13 -0
  70. package/dist/modules/library/errors/library-errors.js.map +1 -0
  71. package/dist/modules/library/index.d.ts +5 -0
  72. package/dist/modules/library/index.d.ts.map +1 -0
  73. package/dist/modules/library/index.js +7 -0
  74. package/dist/modules/library/index.js.map +1 -0
  75. package/dist/modules/library/services/library-svc.d.ts +9 -0
  76. package/dist/modules/library/services/library-svc.d.ts.map +1 -0
  77. package/dist/modules/library/services/library-svc.js +38 -0
  78. package/dist/modules/library/services/library-svc.js.map +1 -0
  79. package/dist/modules/library/types/library-types.d.ts +7 -0
  80. package/dist/modules/library/types/library-types.d.ts.map +1 -0
  81. package/dist/modules/library/types/library-types.js +2 -0
  82. package/dist/modules/library/types/library-types.js.map +1 -0
  83. package/dist/modules/plugin/commands/delete-plugin.d.ts +3 -0
  84. package/dist/modules/plugin/commands/delete-plugin.d.ts.map +1 -0
  85. package/dist/modules/plugin/commands/delete-plugin.js +113 -0
  86. package/dist/modules/plugin/commands/delete-plugin.js.map +1 -0
  87. package/dist/modules/plugin/errors/plugin-errors.d.ts +12 -0
  88. package/dist/modules/plugin/errors/plugin-errors.d.ts.map +1 -1
  89. package/dist/modules/plugin/errors/plugin-errors.js +18 -0
  90. package/dist/modules/plugin/errors/plugin-errors.js.map +1 -1
  91. package/dist/modules/plugin/index.d.ts +1 -0
  92. package/dist/modules/plugin/index.d.ts.map +1 -1
  93. package/dist/modules/plugin/index.js +1 -0
  94. package/dist/modules/plugin/index.js.map +1 -1
  95. package/dist/modules/plugin/lib/plugin-resolver.d.ts.map +1 -1
  96. package/dist/modules/plugin/lib/plugin-resolver.js +13 -2
  97. package/dist/modules/plugin/lib/plugin-resolver.js.map +1 -1
  98. package/dist/modules/plugin/lib/plugin-resolver.test.js +2 -0
  99. package/dist/modules/plugin/lib/plugin-resolver.test.js.map +1 -1
  100. package/dist/modules/plugin/services/plugin-svc.d.ts +5 -1
  101. package/dist/modules/plugin/services/plugin-svc.d.ts.map +1 -1
  102. package/dist/modules/plugin/services/plugin-svc.js +44 -3
  103. package/dist/modules/plugin/services/plugin-svc.js.map +1 -1
  104. package/dist/modules/plugin/services/plugin-svc.test.js +12 -6
  105. package/dist/modules/plugin/services/plugin-svc.test.js.map +1 -1
  106. package/dist/modules/plugin/types/plugin-types.d.ts +6 -0
  107. package/dist/modules/plugin/types/plugin-types.d.ts.map +1 -1
  108. package/dist/templates/plugin/package.json.hbs +1 -1
  109. package/dist/templates/plugin/plugin.json.hbs +1 -3
  110. package/dist/utils/validation.d.ts +3 -7
  111. package/dist/utils/validation.d.ts.map +1 -1
  112. package/dist/utils/validation.js +7 -31
  113. package/dist/utils/validation.js.map +1 -1
  114. package/package.json +1 -1
  115. package/src/cli.ts +16 -17
  116. package/src/infrastructure/package-resolver.test.ts +3 -3
  117. package/src/infrastructure/package-resolver.ts +31 -2
  118. package/src/infrastructure/template.ts +0 -1
  119. package/src/modules/app/commands/create-app.ts +9 -17
  120. package/src/modules/app/commands/delete-app.ts +2 -2
  121. package/src/modules/app/index.ts +3 -5
  122. package/src/modules/app/lib/app-template-resolver.ts +16 -0
  123. package/src/modules/app/lib/manifest-schema.ts +0 -5
  124. package/src/modules/app/services/app-svc.ts +46 -7
  125. package/src/modules/app/services/manifest-svc.ts +8 -57
  126. package/src/modules/app/types/app-types.ts +2 -9
  127. package/src/modules/catalog/commands/generate.ts +1 -1
  128. package/src/modules/catalog/commands/scan.ts +4 -4
  129. package/src/modules/catalog/services/catalog-svc.ts +4 -4
  130. package/src/modules/deploy/commands/deploy-init-action.ts +23 -20
  131. package/src/modules/deploy/commands/deploy-logs-action.ts +19 -13
  132. package/src/modules/deploy/commands/deploy-status-action.ts +19 -13
  133. package/src/modules/library/commands/delete-library.ts +126 -0
  134. package/src/modules/library/errors/library-errors.ts +13 -0
  135. package/src/modules/library/index.ts +11 -0
  136. package/src/modules/library/services/library-svc.ts +50 -0
  137. package/src/modules/library/types/library-types.ts +7 -0
  138. package/src/modules/plugin/commands/delete-plugin.ts +128 -0
  139. package/src/modules/plugin/errors/plugin-errors.ts +20 -0
  140. package/src/modules/plugin/index.ts +1 -0
  141. package/src/modules/plugin/lib/plugin-resolver.test.ts +2 -0
  142. package/src/modules/plugin/lib/plugin-resolver.ts +13 -2
  143. package/src/modules/plugin/services/plugin-svc.test.ts +12 -6
  144. package/src/modules/plugin/services/plugin-svc.ts +55 -5
  145. package/src/modules/plugin/types/plugin-types.ts +8 -0
  146. package/src/utils/validation.ts +10 -38
  147. package/templates/plugin/package.json.hbs +1 -1
  148. package/templates/plugin/plugin.json.hbs +1 -3
  149. package/dist/modules/app/commands/generate-manifest.d.ts +0 -3
  150. package/dist/modules/app/commands/generate-manifest.d.ts.map +0 -1
  151. package/dist/modules/app/commands/generate-manifest.js +0 -62
  152. package/dist/modules/app/commands/generate-manifest.js.map +0 -1
  153. package/dist/modules/app/commands/validate-manifest.d.ts +0 -3
  154. package/dist/modules/app/commands/validate-manifest.d.ts.map +0 -1
  155. package/dist/modules/app/commands/validate-manifest.js +0 -68
  156. package/dist/modules/app/commands/validate-manifest.js.map +0 -1
  157. package/dist/modules/startup/commands/create-startup.d.ts +0 -3
  158. package/dist/modules/startup/commands/create-startup.d.ts.map +0 -1
  159. package/dist/modules/startup/commands/create-startup.js +0 -43
  160. package/dist/modules/startup/commands/create-startup.js.map +0 -1
  161. package/dist/modules/startup/errors/startup-errors.d.ts +0 -13
  162. package/dist/modules/startup/errors/startup-errors.d.ts.map +0 -1
  163. package/dist/modules/startup/errors/startup-errors.js +0 -25
  164. package/dist/modules/startup/errors/startup-errors.js.map +0 -1
  165. package/dist/modules/startup/index.d.ts +0 -5
  166. package/dist/modules/startup/index.d.ts.map +0 -1
  167. package/dist/modules/startup/index.js +0 -7
  168. package/dist/modules/startup/index.js.map +0 -1
  169. package/dist/modules/startup/services/startup-service.d.ts +0 -7
  170. package/dist/modules/startup/services/startup-service.d.ts.map +0 -1
  171. package/dist/modules/startup/services/startup-service.js +0 -43
  172. package/dist/modules/startup/services/startup-service.js.map +0 -1
  173. package/dist/modules/startup/types/startup-types.d.ts +0 -8
  174. package/dist/modules/startup/types/startup-types.d.ts.map +0 -1
  175. package/dist/modules/startup/types/startup-types.js +0 -2
  176. package/dist/modules/startup/types/startup-types.js.map +0 -1
  177. package/dist/modules/startup/utils/startup-validators.d.ts +0 -8
  178. package/dist/modules/startup/utils/startup-validators.d.ts.map +0 -1
  179. package/dist/modules/startup/utils/startup-validators.js +0 -17
  180. package/dist/modules/startup/utils/startup-validators.js.map +0 -1
  181. package/dist/templates/startup/apps/.gitkeep +0 -8
  182. package/dist/utils/monorepo.d.ts +0 -19
  183. package/dist/utils/monorepo.d.ts.map +0 -1
  184. package/dist/utils/monorepo.js +0 -100
  185. package/dist/utils/monorepo.js.map +0 -1
  186. package/src/modules/app/commands/generate-manifest.ts +0 -75
  187. package/src/modules/app/commands/validate-manifest.ts +0 -78
  188. package/src/modules/startup/commands/create-startup.ts +0 -53
  189. package/src/modules/startup/errors/startup-errors.ts +0 -23
  190. package/src/modules/startup/index.ts +0 -11
  191. package/src/modules/startup/services/startup-service.ts +0 -57
  192. package/src/modules/startup/types/startup-types.ts +0 -8
  193. package/src/modules/startup/utils/startup-validators.ts +0 -19
  194. package/src/utils/monorepo.ts +0 -137
  195. package/templates/startup/apps/.gitkeep +0 -8
package/src/cli.ts CHANGED
@@ -2,15 +2,15 @@
2
2
 
3
3
  import { Command } from 'commander'
4
4
 
5
- import { createAppCommand, deleteAppCommand, generateManifestCommand, validateManifestCommand } from './modules/app/index.js'
6
- import { scanCommand, createGenerateCommand } from './modules/catalog/index.js'
7
- import { deployInitCommand, deployLogsCommand, deployStatusCommand } from './modules/deploy/index.js'
5
+ import { appCreateCommand, appDeleteCommand } from './modules/app/index.js'
6
+ // import { catalogScanCommand, catalogGenerateCommand } from './modules/catalog/index.js'
7
+ // import { deployInitCommand, deployLogsCommand, deployStatusCommand } from './modules/deploy/index.js'
8
8
  import { gitConnectCommand } from './modules/git/index.js'
9
- import { pluginInstallCommand, pluginCreateCommand } from './modules/plugin/index.js'
9
+ import { libraryDeleteCommand } from './modules/library/index.js'
10
+ import { pluginInstallCommand, pluginCreateCommand, pluginDeleteCommand } from './modules/plugin/index.js'
10
11
  import { releaseInitCommand } from './modules/release/index.js'
11
- import { createStartupCommand } from './modules/startup/index.js'
12
- import { getPackageVersion } from './utils/version.js'
13
12
  import { initWorkspaceCommand } from './modules/workspace/index.js'
13
+ import { getPackageVersion } from './utils/version.js'
14
14
 
15
15
  const program = new Command()
16
16
  .name('launch77')
@@ -19,15 +19,14 @@ const program = new Command()
19
19
 
20
20
  // Register module commands
21
21
  program.addCommand(initWorkspaceCommand())
22
- program.addCommand(createStartupCommand())
23
- program.addCommand(createAppCommand())
24
- program.addCommand(deleteAppCommand())
25
- program.addCommand(generateManifestCommand())
26
- program.addCommand(validateManifestCommand())
27
- program.addCommand(pluginInstallCommand())
22
+ program.addCommand(appCreateCommand())
23
+ program.addCommand(appDeleteCommand())
24
+ program.addCommand(libraryDeleteCommand())
28
25
  program.addCommand(pluginCreateCommand())
29
- program.addCommand(scanCommand())
30
- program.addCommand(createGenerateCommand())
26
+ program.addCommand(pluginDeleteCommand())
27
+ program.addCommand(pluginInstallCommand())
28
+ // program.addCommand(catalogGenerateCommand())
29
+ // program.addCommand(catalogScanCommand())
31
30
 
32
31
  // Git commands
33
32
  program.addCommand(gitConnectCommand())
@@ -36,8 +35,8 @@ program.addCommand(gitConnectCommand())
36
35
  program.addCommand(releaseInitCommand())
37
36
 
38
37
  // Deploy commands
39
- program.addCommand(deployInitCommand())
40
- program.addCommand(deployStatusCommand())
41
- program.addCommand(deployLogsCommand())
38
+ // program.addCommand(deployInitCommand())
39
+ // program.addCommand(deployStatusCommand())
40
+ // program.addCommand(deployLogsCommand())
42
41
 
43
42
  program.parse()
@@ -177,7 +177,7 @@ describe('PackageResolver', () => {
177
177
  const packageName = 'my-local-package'
178
178
  const localPath = path.join(tempDir, 'test-packages', packageName)
179
179
  await fs.ensureDir(localPath)
180
- await fs.writeJSON(path.join(localPath, 'package.json'), { name: packageName })
180
+ await fs.writeJSON(path.join(localPath, 'package.json'), { name: packageName, version: '0.0.1' })
181
181
 
182
182
  const result = await resolver.resolveLocation(packageName, tempDir)
183
183
 
@@ -216,7 +216,7 @@ describe('PackageResolver', () => {
216
216
  const packageName = 'my-package'
217
217
  const localPath = path.join(tempDir, 'test-packages', packageName)
218
218
  await fs.ensureDir(localPath)
219
- await fs.writeJSON(path.join(localPath, 'package.json'), { name: packageName })
219
+ await fs.writeJSON(path.join(localPath, 'package.json'), { name: packageName, version: '0.0.1' })
220
220
 
221
221
  const result = await resolver.resolveLocation(' my-package ', tempDir)
222
222
 
@@ -301,7 +301,7 @@ describe('PackageResolver', () => {
301
301
  const packageName = 'my-package'
302
302
  const localPath = path.join(customRoot, 'test-packages', packageName)
303
303
  await fs.ensureDir(localPath)
304
- await fs.writeJSON(path.join(localPath, 'package.json'), { name: packageName })
304
+ await fs.writeJSON(path.join(localPath, 'package.json'), { name: packageName, version: '0.0.1' })
305
305
 
306
306
  const result = await resolver.resolveLocation(packageName, customRoot)
307
307
 
@@ -17,6 +17,8 @@ export interface PackageResolution {
17
17
  localPath?: string
18
18
  /** The npm package name if source is 'npm' */
19
19
  npmPackage?: string
20
+ /** The version from package.json (required for local packages, undefined for npm until installed) */
21
+ version?: string
20
22
  }
21
23
 
22
24
  /**
@@ -127,6 +129,30 @@ export abstract class PackageResolver {
127
129
  return `${this.getPackagePrefix()}${trimmedName}`
128
130
  }
129
131
 
132
+ /**
133
+ * Read version from package.json
134
+ * @param packagePath - The path to the package directory
135
+ * @returns The version string from package.json
136
+ * @throws If package.json doesn't exist, can't be read, or is missing the version field
137
+ */
138
+ private async readVersion(packagePath: string): Promise<string> {
139
+ const packageJsonPath = path.join(packagePath, 'package.json')
140
+ try {
141
+ const packageJson = await fs.readJson(packageJsonPath)
142
+ if (!packageJson.version) {
143
+ throw new Error(`Invalid package structure: package.json at ${packagePath} is missing required version field. ` + `All Launch77 packages must include a valid package.json with a version field.`)
144
+ }
145
+ return packageJson.version
146
+ } catch (error) {
147
+ // Re-throw our own error messages
148
+ if (error instanceof Error && error.message.includes('Invalid package structure')) {
149
+ throw error
150
+ }
151
+ // File not found or invalid JSON
152
+ throw new Error(`Invalid package structure: package.json not found or invalid at ${packagePath}. ` + `All Launch77 packages must include a valid package.json with a version field.`)
153
+ }
154
+ }
155
+
130
156
  /**
131
157
  * Resolve package location from name
132
158
  *
@@ -134,15 +160,16 @@ export abstract class PackageResolver {
134
160
  * 1. Check local workspace directory (configured by getFolderName())
135
161
  * 2. Verify local package is valid (using verify())
136
162
  * 3. Fall back to npm package name (with configured prefix)
163
+ * 4. Read version from package.json (if available)
137
164
  *
138
165
  * @param name - The package name to resolve
139
166
  * @param workspaceRoot - The workspace root directory
140
- * @returns PackageResolution with source and resolved location
167
+ * @returns PackageResolution with source, resolved location, and version
141
168
  *
142
169
  * @example
143
170
  * // Local package found
144
171
  * await resolveLocation('my-package', '/workspace')
145
- * // { source: 'local', resolvedName: 'my-package', localPath: '/workspace/plugins/my-package' }
172
+ * // { source: 'local', resolvedName: 'my-package', localPath: '/workspace/plugins/my-package', version: '1.0.0' }
146
173
  *
147
174
  * // Not found locally, resolve to npm
148
175
  * await resolveLocation('release', '/workspace')
@@ -174,10 +201,12 @@ export abstract class PackageResolver {
174
201
  const isValid = await this.verify(localPath)
175
202
 
176
203
  if (isValid) {
204
+ const version = await this.readVersion(localPath)
177
205
  return {
178
206
  source: 'local',
179
207
  resolvedName: trimmedName,
180
208
  localPath,
209
+ version,
181
210
  }
182
211
  }
183
212
  }
@@ -6,7 +6,6 @@ import Handlebars from 'handlebars'
6
6
 
7
7
  export interface TemplateContext {
8
8
  appName: string
9
- startupName?: string
10
9
  port?: string
11
10
  [key: string]: string | number | boolean | undefined
12
11
  }
@@ -7,27 +7,19 @@ import ora from 'ora'
7
7
 
8
8
  import { detectLaunch77Context } from '@launch77/plugin-runtime'
9
9
  import { AppService } from '../services/app-svc.js'
10
- import { APP_TYPES, APP_TYPES_LIST } from '../types/app-types.js'
11
10
 
12
- import type { AppType } from '../types/app-types.js'
13
-
14
- export function createAppCommand(): Command {
11
+ export function appCreateCommand(): Command {
15
12
  const command = new Command('app:create')
16
- .argument('<type>', 'App type (api, webapp, marketing-site)')
13
+ .argument('<template>', 'App template name (e.g., webapp, api, marketing-site)')
17
14
  .argument('<app-name>', 'Name of the application')
18
- .description('Create a new application')
19
- .option('-p, --port <port>', 'Default port for the app')
20
- .action(async (type: string, appName: string, options) => {
15
+ .description('Create a new application from a template')
16
+ .option('-p, --port <port>', 'Default port for the app (default: 3000)')
17
+ .action(async (template: string, appName: string, options) => {
21
18
  try {
22
- // Validate app type
23
- if (!APP_TYPES.includes(type as AppType)) {
24
- throw new Error(`Invalid app type: ${type}\n\nSupported types: ${APP_TYPES_LIST}`)
25
- }
26
-
27
- console.log(chalk.blue(`\n🚀 Creating ${type} app: ${appName}\n`))
19
+ console.log(chalk.blue(`\n🚀 Creating app: ${appName} from template: ${template}\n`))
28
20
 
29
- // Set default port based on app type
30
- const port = options.port || (type === 'marketing-site' || type === 'webapp' ? '3000' : '4000')
21
+ // Set default port
22
+ const port = options.port || '3000'
31
23
 
32
24
  // Detect context
33
25
  const context = await detectLaunch77Context(process.cwd())
@@ -39,7 +31,7 @@ export function createAppCommand(): Command {
39
31
  const templateSpinner = ora('Generating app structure...').start()
40
32
  let result
41
33
  try {
42
- result = await appService.createApp({ type: type as AppType, appName, port }, context)
34
+ result = await appService.createApp({ type: template, appName, port }, context)
43
35
  templateSpinner.succeed('App structure generated')
44
36
  } catch (error) {
45
37
  templateSpinner.fail('Failed to generate app structure')
@@ -9,10 +9,10 @@ import ora from 'ora'
9
9
  import { detectLaunch77Context } from '@launch77/plugin-runtime'
10
10
  import { AppService } from '../services/app-svc.js'
11
11
 
12
- export function deleteAppCommand(): Command {
12
+ export function appDeleteCommand(): Command {
13
13
  const command = new Command('app:delete')
14
14
  .argument('<app-name>', 'Name of the app to delete')
15
- .description('Delete an app from the startup (requires confirmation)')
15
+ .description('Delete an app from the workspace (requires confirmation)')
16
16
  .action(async (appName: string) => {
17
17
  try {
18
18
  console.log(chalk.yellow(`\n⚠️ WARNING: You are about to delete the app '${appName}'\n`))
@@ -3,14 +3,12 @@ export { AppService } from './services/app-svc.js'
3
3
  export { ManifestService } from './services/manifest-svc.js'
4
4
 
5
5
  // Types
6
- export type { CreateAppRequest, CreateAppResult, DeleteAppRequest, DeleteAppResult, AppType } from './types/app-types.js'
6
+ export type { CreateAppRequest, CreateAppResult, DeleteAppRequest, DeleteAppResult } from './types/app-types.js'
7
7
  export type { AppManifest, DeploymentConfig } from './lib/manifest-schema.js'
8
8
 
9
9
  // Errors
10
10
  export { AppAlreadyExistsError, AppNotFoundError, InvalidAppNameError, TemplateNotFoundError, ManifestNotFoundError, InvalidManifestError } from './errors/app-errors.js'
11
11
 
12
12
  // Commands
13
- export { createAppCommand } from './commands/create-app.js'
14
- export { deleteAppCommand } from './commands/delete-app.js'
15
- export { generateManifestCommand } from './commands/generate-manifest.js'
16
- export { validateManifestCommand } from './commands/validate-manifest.js'
13
+ export { appCreateCommand } from './commands/create-app.js'
14
+ export { appDeleteCommand } from './commands/delete-app.js'
@@ -21,6 +21,22 @@ export class AppTemplateResolver extends PackageResolver {
21
21
  }
22
22
 
23
23
  protected async verify(localPath: string): Promise<boolean> {
24
+ // All Launch77 packages must have package.json with version
25
+ const hasPackageJson = await fs.pathExists(path.join(localPath, 'package.json'))
26
+ if (!hasPackageJson) {
27
+ return false
28
+ }
29
+
30
+ // Verify package.json has a version field
31
+ try {
32
+ const packageJson = await fs.readJson(path.join(localPath, 'package.json'))
33
+ if (!packageJson.version) {
34
+ return false
35
+ }
36
+ } catch {
37
+ return false
38
+ }
39
+
24
40
  // Check for either:
25
41
  // 1. A 'template' subdirectory (preferred structure for npm packages)
26
42
  // 2. Any files at the root (simple local templates)
@@ -1,7 +1,5 @@
1
1
  import { z } from 'zod'
2
2
 
3
- import type { AppType } from '../types/app-types.js'
4
-
5
3
  // Deployment configuration
6
4
  export type DeploymentConfig = {
7
5
  platform: 'vercel'
@@ -14,7 +12,6 @@ export type DeploymentConfig = {
14
12
  export type AppManifest = {
15
13
  schemaVersion: 1
16
14
  name: string
17
- type: AppType | 'marketing' // Support both 'marketing' and 'marketing-site'
18
15
  package: string
19
16
  deployment?: DeploymentConfig
20
17
  }
@@ -28,12 +25,10 @@ const deploymentConfigZ = z.object({
28
25
  })
29
26
 
30
27
  // Main manifest schema - strict mode to reject extra fields
31
- // Note: Manifests store 'marketing' but we accept 'marketing-site' in commands
32
28
  export const manifestZ = z
33
29
  .object({
34
30
  schemaVersion: z.literal(1),
35
31
  name: z.string().min(1),
36
- type: z.enum(['marketing', 'webapp', 'api']),
37
32
  package: z.string().min(1),
38
33
  deployment: deploymentConfigZ.optional(),
39
34
  })
@@ -1,6 +1,7 @@
1
1
  import * as path from 'path'
2
2
 
3
3
  import fs from 'fs-extra'
4
+ import { readTemplateMetadata } from '@launch77/plugin-runtime'
4
5
 
5
6
  import { ManifestService } from './manifest-svc.js'
6
7
  import * as filesystem from '../../../infrastructure/filesystem.js'
@@ -12,7 +13,7 @@ import { validateAppName } from '../../../utils/validation.js'
12
13
  import { AppTemplateResolver } from '../lib/app-template-resolver.js'
13
14
 
14
15
  import type { CreateAppRequest, CreateAppResult, DeleteAppRequest, DeleteAppResult } from '../types/app-types.js'
15
- import type { Launch77Context } from '@launch77/plugin-runtime'
16
+ import type { Launch77Context, TemplateReference, Launch77PackageManifest } from '@launch77/plugin-runtime'
16
17
 
17
18
  export class AppService {
18
19
  private manifestService: ManifestService
@@ -51,9 +52,14 @@ export class AppService {
51
52
  const templateResolution = await this.appTemplateResolver.resolveLocation(type, context.workspaceRoot)
52
53
 
53
54
  let templatePath: string
55
+ let templateVersion: string
56
+ let packageRootPath: string
57
+
54
58
  if (templateResolution.source === 'local') {
55
59
  // Use local template
56
- templatePath = templateResolution.localPath!
60
+ packageRootPath = templateResolution.localPath!
61
+ templatePath = packageRootPath
62
+ templateVersion = templateResolution.version! // Local templates always have version after verification
57
63
  } else {
58
64
  // Download npm template
59
65
  const downloadResult = await downloadNpmPackage({
@@ -61,13 +67,20 @@ export class AppService {
61
67
  workspaceRoot: context.workspaceRoot,
62
68
  })
63
69
 
70
+ packageRootPath = downloadResult.packagePath
71
+
64
72
  // Check if template has a 'template' subdirectory, otherwise use package root
65
- const templateSubdir = path.join(downloadResult.packagePath, 'template')
73
+ const templateSubdir = path.join(packageRootPath, 'template')
66
74
  if (await fs.pathExists(templateSubdir)) {
67
75
  templatePath = templateSubdir
68
76
  } else {
69
- templatePath = downloadResult.packagePath
77
+ templatePath = packageRootPath
70
78
  }
79
+
80
+ // Read version from downloaded package
81
+ const packageJsonPath = path.join(packageRootPath, 'package.json')
82
+ const packageJson = await fs.readJson(packageJsonPath)
83
+ templateVersion = packageJson.version
71
84
  }
72
85
 
73
86
  // 5. Determine app path
@@ -91,14 +104,40 @@ export class AppService {
91
104
  port,
92
105
  })
93
106
 
94
- // 9. Generate and write app manifest
95
- const manifest = this.manifestService.generateManifest(type, appName, context, { port })
107
+ // 9. Read template metadata (currently empty, placeholder for future config)
108
+ //const templateMetadata = await readTemplateMetadata(templatePath)
109
+
110
+ // 10. Write template reference to app's package.json
111
+ const appPackageJsonPath = path.join(appPath, 'package.json')
112
+ const appPackageJson = await fs.readJson(appPackageJsonPath)
113
+
114
+ // Build template reference - version read from package.json
115
+ const templatePackage = templateResolution.source === 'npm' ? templateResolution.npmPackage! : templateResolution.resolvedName
116
+ const templateRef: TemplateReference = {
117
+ package: templatePackage,
118
+ version: templateVersion,
119
+ source: templateResolution.source,
120
+ createdAt: new Date().toISOString(),
121
+ }
122
+
123
+ // Initialize launch77 manifest structure
124
+ if (!appPackageJson.launch77) {
125
+ appPackageJson.launch77 = {}
126
+ }
127
+ const launch77Manifest = appPackageJson.launch77 as Launch77PackageManifest
128
+ launch77Manifest.template = templateRef
129
+
130
+ // Write updated package.json
131
+ await fs.writeJson(appPackageJsonPath, appPackageJson, { spaces: 2 })
132
+
133
+ // 11. Generate and write app manifest
134
+ const manifest = this.manifestService.generateManifest(appName, context, { port })
96
135
  const launchDir = path.join(appPath, '.launch')
97
136
  await filesystem.ensureDir(launchDir)
98
137
  const manifestPath = path.join(launchDir, 'app.json')
99
138
  await filesystem.writeJSON(manifestPath, manifest)
100
139
 
101
- // 10. Install dependencies
140
+ // 12. Install dependencies
102
141
  await npm.install(context.workspaceRoot)
103
142
 
104
143
  return {
@@ -8,7 +8,7 @@ import type { AppManifest } from '../lib/manifest-schema.js'
8
8
 
9
9
  /**
10
10
  * Convert kebab-case to Title Case
11
- * e.g., "my-startup-name" -> "My Startup Name"
11
+ * e.g., "my-workspace-name" -> "My Workspace Name"
12
12
  */
13
13
  function toTitleCase(str: string): string {
14
14
  return str
@@ -17,66 +17,17 @@ function toTitleCase(str: string): string {
17
17
  .join(' ')
18
18
  }
19
19
 
20
- /**
21
- * Generate a manifest for a marketing app
22
- */
23
- function generateMarketingManifest(appName: string, context: Launch77Context): AppManifest {
24
- const workspaceName = context.workspaceName
25
-
26
- return {
27
- schemaVersion: 1,
28
- name: `${toTitleCase(workspaceName)} Marketing`,
29
- type: 'marketing',
30
- package: `@${workspaceName}/${appName}`,
31
- }
32
- }
33
-
34
- /**
35
- * Generate a manifest for a webapp
36
- */
37
- function generateWebappManifest(appName: string, context: Launch77Context): AppManifest {
38
- const workspaceName = context.workspaceName
39
-
40
- return {
41
- schemaVersion: 1,
42
- name: `${toTitleCase(workspaceName)} Web`,
43
- type: 'webapp',
44
- package: `@${workspaceName}/${appName}`,
45
- }
46
- }
47
-
48
- /**
49
- * Generate a manifest for an API app
50
- */
51
- function generateApiManifest(appName: string, context: Launch77Context): AppManifest {
52
- const workspaceName = context.workspaceName
53
-
54
- return {
55
- schemaVersion: 1,
56
- name: `${toTitleCase(workspaceName)} API`,
57
- type: 'api',
58
- package: `@${workspaceName}/${appName}`,
59
- }
60
- }
61
-
62
20
  export class ManifestService {
63
21
  /**
64
- * Generate an app manifest based on app type
22
+ * Generate an app manifest
65
23
  */
66
- generateManifest(type: string, appName: string, context: Launch77Context, _options: { port?: string }): AppManifest {
67
- switch (type) {
68
- case 'marketing-site':
69
- case 'marketing':
70
- return generateMarketingManifest(appName, context)
71
-
72
- case 'webapp':
73
- return generateWebappManifest(appName, context)
74
-
75
- case 'api':
76
- return generateApiManifest(appName, context)
24
+ generateManifest(appName: string, context: Launch77Context, _options: { port?: string }): AppManifest {
25
+ const workspaceName = context.workspaceName
77
26
 
78
- default:
79
- throw new Error(`Unsupported app type for manifest generation: ${type}. Supported types: api, webapp, marketing-site`)
27
+ return {
28
+ schemaVersion: 1,
29
+ name: toTitleCase(workspaceName),
30
+ package: `@${workspaceName}/${appName}`,
80
31
  }
81
32
  }
82
33
 
@@ -1,12 +1,5 @@
1
- // Supported app types
2
- export const APP_TYPES = ['api', 'webapp', 'marketing-site'] as const
3
- export type AppType = (typeof APP_TYPES)[number]
4
-
5
- // Display list for error messages
6
- export const APP_TYPES_LIST = APP_TYPES.join(', ')
7
-
8
1
  export interface CreateAppRequest {
9
- type: AppType
2
+ type: string
10
3
  appName: string
11
4
  port: string
12
5
  }
@@ -14,7 +7,7 @@ export interface CreateAppRequest {
14
7
  export interface CreateAppResult {
15
8
  appPath: string
16
9
  appName: string
17
- type: AppType
10
+ type: string
18
11
  }
19
12
 
20
13
  export interface DeleteAppRequest {
@@ -21,7 +21,7 @@ interface GenerateOptions {
21
21
  /**
22
22
  * Create the catalog:generate command
23
23
  */
24
- export function createGenerateCommand(): Command {
24
+ export function catalogGenerateCommand(): Command {
25
25
  const command = new Command('catalog:generate')
26
26
  .description('Generate catalog metadata for a component library')
27
27
  .option('-l, --library <path>', 'Library directory path', process.cwd())
@@ -1,7 +1,7 @@
1
1
  import chalk from 'chalk'
2
2
  import { Command } from 'commander'
3
+ import { detectLaunch77Context } from '@launch77/plugin-runtime'
3
4
 
4
- import { detectMonorepoContext } from '../../../utils/monorepo.js'
5
5
  import { CatalogService } from '../services/catalog-svc.js'
6
6
 
7
7
  /**
@@ -10,17 +10,17 @@ import { CatalogService } from '../services/catalog-svc.js'
10
10
  * Scans UI components, libraries, plugins, and utilities from the Launch77 monorepo
11
11
  * and generates a machine-readable catalog for AI discovery.
12
12
  */
13
- export function scanCommand(): Command {
13
+ export function catalogScanCommand(): Command {
14
14
  const command = new Command('catalog:scan')
15
15
  .description('Scan Launch77 artifacts and generate catalog.json')
16
16
  .option('--no-strict', 'Skip validation errors and generate catalog anyway')
17
17
  .option('--quiet', 'Suppress quality report output')
18
18
  .action(async (options) => {
19
19
  try {
20
- const context = await detectMonorepoContext(process.cwd())
20
+ const context = await detectLaunch77Context(process.cwd())
21
21
 
22
22
  if (!context.isValid) {
23
- console.error(chalk.red('\n✖ Must be run from within a Launch77 monorepo\n'))
23
+ console.error(chalk.red('\n✖ Must be run from within a Launch77 workspace\n'))
24
24
  process.exit(1)
25
25
  }
26
26
 
@@ -9,7 +9,7 @@ import { scanUILibrary } from '../scanners/ui-component-scanner.js'
9
9
  import { generateExamplesFiles } from '../utils/examples-generator.js'
10
10
  import { generateQualityReport, printQualityReport } from '../utils/quality-reporter.js'
11
11
 
12
- import type { MonorepoContext } from '../../../utils/monorepo.js'
12
+ import type { Launch77Context } from '@launch77/plugin-runtime'
13
13
  import type { ValidationResult, UiComponent } from '../types/catalog-types.js'
14
14
 
15
15
  export interface ScanCatalogOptions {
@@ -32,13 +32,13 @@ export class CatalogService {
32
32
  /**
33
33
  * Scan Launch77 codebase and generate catalog.json
34
34
  */
35
- async scanCatalog(context: MonorepoContext, options: ScanCatalogOptions = {}): Promise<ScanCatalogResult> {
35
+ async scanCatalog(context: Launch77Context, options: ScanCatalogOptions = {}): Promise<ScanCatalogResult> {
36
36
  const strict = options.strict !== false // Default to true (strict mode)
37
37
  const quiet = options.quiet || false
38
38
 
39
39
  console.log(chalk.blue('\n🔍 Scanning Launch77 artifacts...\n'))
40
40
 
41
- const launch77Root = path.join(context.monorepoRoot, 'launch77')
41
+ const launch77Root = path.join(context.workspaceRoot, 'launch77')
42
42
 
43
43
  const spinner = ora('Scanning codebase...').start()
44
44
 
@@ -109,7 +109,7 @@ export class CatalogService {
109
109
  console.log(chalk.green(` ✓ Saved to ${path.relative(process.cwd(), catalogPath)}`))
110
110
 
111
111
  // Sync catalog to Launch77 marketing site
112
- const marketingSiteCatalogPath = path.join(context.monorepoRoot, 'startups/launch77/apps/marketing/src/catalog.json')
112
+ const marketingSiteCatalogPath = path.join(context.workspaceRoot, 'apps/marketing/src/catalog.json')
113
113
 
114
114
  if (await fs.pathExists(path.dirname(marketingSiteCatalogPath))) {
115
115
  await fs.writeFile(marketingSiteCatalogPath, formatted)