@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
@@ -6,10 +6,10 @@
6
6
  import * as path from 'path'
7
7
 
8
8
  import chalk from 'chalk'
9
+ import { detectLaunch77Context } from '@launch77/plugin-runtime'
9
10
  import fs from 'fs-extra'
10
11
  import ora from 'ora'
11
12
 
12
- import { detectMonorepoContext } from '../../../utils/monorepo.js'
13
13
  import { DeployService } from '../services/deploy-svc.js'
14
14
  import { connectGitRepository, getPackageName, getVercelInstallInstructions, isVercelCliInstalled, loadVercelProject, runVercelCommand, updateProjectRootDirectory, verifyVercelProjectExists } from '../utils/vercel-extended.js'
15
15
 
@@ -23,16 +23,15 @@ interface DeployInitOptions {
23
23
  export async function deployInit(options: DeployInitOptions = {}) {
24
24
  const deployService = new DeployService()
25
25
 
26
- // Detect monorepo context
27
- const context = await detectMonorepoContext(process.cwd())
26
+ // Detect Launch77 context
27
+ const context = await detectLaunch77Context(process.cwd())
28
28
 
29
- if (context.location !== 'startup-app') {
30
- throw new Error('Must be run from within an app directory (e.g., startups/mycompany/apps/myapp)')
29
+ if (context.locationType !== 'workspace-app') {
30
+ throw new Error('Must be run from within an app directory (e.g., apps/myapp)')
31
31
  }
32
32
 
33
33
  const appPath = process.cwd()
34
34
  const appName = context.appName!
35
- const startupName = context.startupName!
36
35
 
37
36
  console.log(chalk.blue(`\n🚀 Initializing Vercel deployment for ${appName}...\n`))
38
37
 
@@ -116,23 +115,27 @@ export async function deployInit(options: DeployInitOptions = {}) {
116
115
  throw new Error('Could not read package name from package.json')
117
116
  }
118
117
 
118
+ // Check template type from app's package.json
119
+ const appPackageJsonPath = path.join(appPath, 'package.json')
120
+ const appPackageJson = await fs.readJson(appPackageJsonPath)
121
+ const templatePackage = appPackageJson.launch77?.template?.package
122
+
119
123
  console.log(chalk.gray(`App details:`))
120
124
  console.log(chalk.gray(` Name: ${manifest.name}`))
121
- console.log(chalk.gray(` Type: ${manifest.type}`))
122
125
  console.log(chalk.gray(` Package: ${packageName}`))
126
+ if (templatePackage) {
127
+ console.log(chalk.gray(` Template: ${templatePackage}`))
128
+ }
123
129
  console.log()
124
130
 
125
- // Check if this is an API app - Railway deployment coming soon
126
- if (manifest.type === 'api') {
127
- console.log(chalk.yellow('⚠️ Railway deployments for API apps are not yet supported'))
128
- console.log()
129
- console.log(chalk.cyan('Coming soon! For now, you can deploy manually to:'))
130
- console.log(chalk.white(' Railway.app'), chalk.gray('- Simple deployment with databases'))
131
- console.log(chalk.white(' • Fly.io'), chalk.gray('- Global edge deployment'))
132
- console.log(chalk.white(' • Render.com'), chalk.gray('- Managed cloud hosting'))
131
+ // Only allow webapp apps to deploy to Vercel
132
+ const isWebapp = templatePackage === 'webapp' || templatePackage === '@launch77-shared/app-template-webapp'
133
+
134
+ if (!isWebapp) {
135
+ console.log(chalk.yellow('⚠️ Vercel deployment only supports webapp apps'))
136
+ console.log(chalk.gray('Other app types coming soon'))
133
137
  console.log()
134
- console.log(chalk.gray('Stay tuned for automated Railway deployment support!'))
135
- process.exit(0)
138
+ process.exit(1)
136
139
  }
137
140
 
138
141
  // Check if vercel.json exists
@@ -160,7 +163,7 @@ export async function deployInit(options: DeployInitOptions = {}) {
160
163
  console.log()
161
164
 
162
165
  try {
163
- const suggestedProjectName = `${startupName}--${appName}`
166
+ const suggestedProjectName = appName
164
167
  await runVercelCommand(['link', '--project', suggestedProjectName, '--yes'], appPath)
165
168
 
166
169
  console.log()
@@ -172,7 +175,7 @@ export async function deployInit(options: DeployInitOptions = {}) {
172
175
 
173
176
  // Read the created .vercel/project.json to get project info
174
177
  const vercelProject = await loadVercelProject(appPath)
175
- const projectName = vercelProject?.projectName || `${startupName}--${appName}`
178
+ const projectName = vercelProject?.projectName || appName
176
179
 
177
180
  // Update manifest with deployment configuration
178
181
  manifest.deployment = {
@@ -187,7 +190,7 @@ export async function deployInit(options: DeployInitOptions = {}) {
187
190
 
188
191
  // Set Root Directory for monorepo deployments
189
192
  console.log()
190
- const rootDirectory = `startups/${startupName}/apps/${appName}`
193
+ const rootDirectory = `apps/${appName}`
191
194
  await updateProjectRootDirectory(projectName, rootDirectory)
192
195
 
193
196
  // Automatically connect to Git repository
@@ -3,9 +3,12 @@
3
3
  */
4
4
 
5
5
  /* eslint-disable no-console */
6
+ import * as path from 'path'
7
+
6
8
  import chalk from 'chalk'
9
+ import { detectLaunch77Context } from '@launch77/plugin-runtime'
10
+ import fs from 'fs-extra'
7
11
 
8
- import { detectMonorepoContext } from '../../../utils/monorepo.js'
9
12
  import { DeployService } from '../services/deploy-svc.js'
10
13
  import { getVercelInstallInstructions, isVercelCliInstalled, runVercelCommand } from '../utils/vercel-extended.js'
11
14
 
@@ -20,11 +23,11 @@ export interface DeployLogsOptions {
20
23
  export async function deployLogs(options: DeployLogsOptions) {
21
24
  const deployService = new DeployService()
22
25
 
23
- // Detect monorepo context
24
- const context = await detectMonorepoContext(process.cwd())
26
+ // Detect Launch77 context
27
+ const context = await detectLaunch77Context(process.cwd())
25
28
 
26
- if (context.location !== 'startup-app') {
27
- throw new Error('Must be run from within an app directory (e.g., startups/mycompany/apps/myapp)')
29
+ if (context.locationType !== 'workspace-app') {
30
+ throw new Error('Must be run from within an app directory (e.g., apps/myapp)')
28
31
  }
29
32
 
30
33
  const appPath = process.cwd()
@@ -34,14 +37,17 @@ export async function deployLogs(options: DeployLogsOptions) {
34
37
  // Load manifest
35
38
  const manifest = await deployService.loadManifest()
36
39
 
37
- // Check if this is an API app
38
- if (manifest.type === 'api') {
39
- console.log(chalk.yellow('⚠️ Railway deployments for API apps are not yet supported'))
40
- console.log()
41
- console.log(chalk.cyan('Coming soon! For now, you can view logs at:'))
42
- console.log(chalk.white(' Railway.app dashboard'))
43
- console.log(chalk.white(' Fly.io dashboard'))
44
- console.log(chalk.white(' • Render.com dashboard'))
40
+ // Check template type from app's package.json
41
+ const appPackageJsonPath = path.join(appPath, 'package.json')
42
+ const appPackageJson = await fs.readJson(appPackageJsonPath)
43
+ const templatePackage = appPackageJson.launch77?.template?.package
44
+
45
+ // Only allow webapp apps to deploy to Vercel
46
+ const isWebapp = templatePackage === 'webapp' || templatePackage === '@launch77-shared/app-template-webapp'
47
+
48
+ if (!isWebapp) {
49
+ console.log(chalk.yellow('⚠️ Vercel deployment only supports webapp apps'))
50
+ console.log(chalk.gray('Other app types coming soon'))
45
51
  console.log()
46
52
  return
47
53
  }
@@ -3,10 +3,13 @@
3
3
  */
4
4
 
5
5
  /* eslint-disable no-console */
6
+ import * as path from 'path'
7
+
6
8
  import chalk from 'chalk'
9
+ import { detectLaunch77Context } from '@launch77/plugin-runtime'
10
+ import fs from 'fs-extra'
7
11
  import ora from 'ora'
8
12
 
9
- import { detectMonorepoContext } from '../../../utils/monorepo.js'
10
13
  import { DeployService } from '../services/deploy-svc.js'
11
14
  import { getVercelInstallInstructions, isVercelCliInstalled, runVercelCommand, verifyVercelProjectExists } from '../utils/vercel-extended.js'
12
15
 
@@ -16,11 +19,11 @@ import { getVercelInstallInstructions, isVercelCliInstalled, runVercelCommand, v
16
19
  export async function deployStatus() {
17
20
  const deployService = new DeployService()
18
21
 
19
- // Detect monorepo context
20
- const context = await detectMonorepoContext(process.cwd())
22
+ // Detect Launch77 context
23
+ const context = await detectLaunch77Context(process.cwd())
21
24
 
22
- if (context.location !== 'startup-app') {
23
- throw new Error('Must be run from within an app directory (e.g., startups/mycompany/apps/myapp)')
25
+ if (context.locationType !== 'workspace-app') {
26
+ throw new Error('Must be run from within an app directory (e.g., apps/myapp)')
24
27
  }
25
28
 
26
29
  const appPath = process.cwd()
@@ -30,14 +33,17 @@ export async function deployStatus() {
30
33
  // Load manifest
31
34
  const manifest = await deployService.loadManifest()
32
35
 
33
- // Check if this is an API app
34
- if (manifest.type === 'api') {
35
- console.log(chalk.yellow('⚠️ Railway deployments for API apps are not yet supported'))
36
- console.log()
37
- console.log(chalk.cyan('Coming soon! For now, you can check your deployment status at:'))
38
- console.log(chalk.white(' Railway.app dashboard'))
39
- console.log(chalk.white(' Fly.io dashboard'))
40
- console.log(chalk.white(' • Render.com dashboard'))
36
+ // Check template type from app's package.json
37
+ const appPackageJsonPath = path.join(appPath, 'package.json')
38
+ const appPackageJson = await fs.readJson(appPackageJsonPath)
39
+ const templatePackage = appPackageJson.launch77?.template?.package
40
+
41
+ // Only allow webapp apps to deploy to Vercel
42
+ const isWebapp = templatePackage === 'webapp' || templatePackage === '@launch77-shared/app-template-webapp'
43
+
44
+ if (!isWebapp) {
45
+ console.log(chalk.yellow('⚠️ Vercel deployment only supports webapp apps'))
46
+ console.log(chalk.gray('Other app types coming soon'))
41
47
  console.log()
42
48
  return
43
49
  }
@@ -0,0 +1,126 @@
1
+ /* eslint-disable no-console */
2
+ import * as path from 'path'
3
+
4
+ import chalk from 'chalk'
5
+ import { Command } from 'commander'
6
+ import inquirer from 'inquirer'
7
+ import ora from 'ora'
8
+
9
+ import { detectLaunch77Context } from '@launch77/plugin-runtime'
10
+ import { LibraryService } from '../services/library-svc.js'
11
+
12
+ export function libraryDeleteCommand(): Command {
13
+ const command = new Command('library:delete')
14
+ .argument('<library-name>', 'Name of the library to delete')
15
+ .description('Delete a library from the workspace (requires confirmation)')
16
+ .action(async (libraryName: string) => {
17
+ try {
18
+ console.log(chalk.yellow(`\n⚠️ WARNING: You are about to delete the library '${libraryName}'\n`))
19
+
20
+ // Detect context
21
+ const context = await detectLaunch77Context(process.cwd())
22
+
23
+ // Create library service
24
+ const libraryService = new LibraryService()
25
+
26
+ // Get library path for display (before deletion)
27
+ const libraryPath = path.join(context.workspaceRoot, 'libraries', libraryName)
28
+
29
+ // Display what will be deleted
30
+ console.log(chalk.red('Library location:'), libraryPath)
31
+ console.log(chalk.red('\nThis will permanently delete:'))
32
+ console.log(chalk.gray(' • All source code and configuration files'))
33
+ console.log(chalk.gray(' • All node_modules (will be cleaned from monorepo)'))
34
+ console.log(chalk.gray(' • All build artifacts'))
35
+
36
+ console.log(chalk.yellow('\n⚠️ Important notes:'))
37
+ console.log(chalk.gray(' • Check if other packages depend on this library'))
38
+ console.log(chalk.gray(' • If published to npm, you may want to deprecate/unpublish it manually'))
39
+ console.log(chalk.gray(' • Update import statements in dependent packages manually'))
40
+
41
+ console.log(chalk.red('\n⚠️ THIS ACTION CANNOT BE UNDONE!\n'))
42
+
43
+ // First confirmation: Type library name
44
+ const { confirmName } = await inquirer.prompt([
45
+ {
46
+ type: 'input',
47
+ name: 'confirmName',
48
+ message: `Type the library name to confirm:`,
49
+ validate: (input: string) => {
50
+ if (input === libraryName) {
51
+ return true
52
+ }
53
+ return `Please type '${libraryName}' exactly to confirm`
54
+ },
55
+ },
56
+ ])
57
+
58
+ if (confirmName !== libraryName) {
59
+ console.log(chalk.gray('\n✖ Deletion cancelled\n'))
60
+ return
61
+ }
62
+
63
+ // Second confirmation: Type DELETE
64
+ const { confirmDelete } = await inquirer.prompt([
65
+ {
66
+ type: 'input',
67
+ name: 'confirmDelete',
68
+ message: 'Type DELETE (all caps) to confirm permanent deletion:',
69
+ validate: (input: string) => {
70
+ if (input === 'DELETE') {
71
+ return true
72
+ }
73
+ return 'Please type DELETE (all caps) to confirm'
74
+ },
75
+ },
76
+ ])
77
+
78
+ if (confirmDelete !== 'DELETE') {
79
+ console.log(chalk.gray('\n✖ Deletion cancelled\n'))
80
+ return
81
+ }
82
+
83
+ // Delete the library
84
+ const deleteSpinner = ora('Deleting library directory...').start()
85
+ try {
86
+ await libraryService.deleteLibrary({ libraryName }, context)
87
+ deleteSpinner.succeed('Library directory deleted')
88
+ } catch (error) {
89
+ deleteSpinner.fail('Failed to delete library directory')
90
+ throw error
91
+ }
92
+
93
+ // Update dependencies
94
+ const installSpinner = ora('Updating workspace dependencies...').start()
95
+ try {
96
+ // This is already done by library service, just show spinner for consistency
97
+ installSpinner.succeed('Dependencies updated (package-lock.json cleaned)')
98
+ } catch (error) {
99
+ installSpinner.fail('Failed to update dependencies')
100
+ console.log(chalk.yellow('\n⚠️ You may need to run "npm install" manually at the workspace root\n'))
101
+ }
102
+
103
+ // Success message
104
+ console.log(chalk.green(`\n✅ Library '${libraryName}' has been deleted\n`))
105
+
106
+ // Reminder about cleanup
107
+ console.log(chalk.yellow('📝 Remember to:'))
108
+ console.log(chalk.gray(' • Check and update dependent packages (remove from dependencies)'))
109
+ console.log(chalk.gray(' • Update import statements in code that used this library'))
110
+ console.log(chalk.gray(' • Consider deprecating/unpublishing from npm if published'))
111
+ console.log()
112
+
113
+ // Reminder about Git
114
+ console.log(chalk.yellow('📝 Remember to commit the deletion:'))
115
+ console.log(chalk.gray(' git add -A'))
116
+ console.log(chalk.gray(` git commit -m "Remove ${libraryName} library"`))
117
+ console.log()
118
+ } catch (error) {
119
+ const message = error instanceof Error ? error.message : String(error)
120
+ console.error(chalk.red('Error:'), message)
121
+ process.exit(1)
122
+ }
123
+ })
124
+
125
+ return command
126
+ }
@@ -0,0 +1,13 @@
1
+ export class LibraryNotFoundError extends Error {
2
+ constructor(libraryName: string, expectedPath: string) {
3
+ super(`Library '${libraryName}' does not exist.\n\n` + `Expected location: ${expectedPath}\n\n` + `Available libraries:\n` + ` cd libraries/\n` + ` ls`)
4
+ this.name = 'LibraryNotFoundError'
5
+ }
6
+ }
7
+
8
+ export class InvalidLibraryNameError extends Error {
9
+ constructor(message: string) {
10
+ super(message)
11
+ this.name = 'InvalidLibraryNameError'
12
+ }
13
+ }
@@ -0,0 +1,11 @@
1
+ // Services
2
+ export { LibraryService } from './services/library-svc.js'
3
+
4
+ // Types
5
+ export type { DeleteLibraryRequest, DeleteLibraryResult } from './types/library-types.js'
6
+
7
+ // Errors
8
+ export { LibraryNotFoundError, InvalidLibraryNameError } from './errors/library-errors.js'
9
+
10
+ // Commands
11
+ export { libraryDeleteCommand } from './commands/delete-library.js'
@@ -0,0 +1,50 @@
1
+ import * as path from 'path'
2
+
3
+ import fs from 'fs-extra'
4
+
5
+ import * as npm from '../../../infrastructure/npm.js'
6
+ import { validateWorkspaceContext } from '../../../utils/launch77-validation.js'
7
+ import { validateAppName } from '../../../utils/validation.js'
8
+ import { LibraryNotFoundError, InvalidLibraryNameError } from '../errors/library-errors.js'
9
+
10
+ import type { DeleteLibraryRequest, DeleteLibraryResult } from '../types/library-types.js'
11
+ import type { Launch77Context } from '@launch77/plugin-runtime'
12
+
13
+ export class LibraryService {
14
+ /**
15
+ * Delete a library from the workspace
16
+ */
17
+ async deleteLibrary(request: DeleteLibraryRequest, context: Launch77Context): Promise<DeleteLibraryResult> {
18
+ const { libraryName } = request
19
+
20
+ // 1. Validate library name (reusing app name validation since format is the same)
21
+ const nameValidation = validateAppName(libraryName)
22
+ if (!nameValidation.valid) {
23
+ throw new InvalidLibraryNameError(nameValidation.errorMessage || 'Invalid library name')
24
+ }
25
+
26
+ // 2. Validate workspace context
27
+ const contextValidation = validateWorkspaceContext(context)
28
+ if (!contextValidation.valid) {
29
+ throw new Error(contextValidation.errorMessage || 'Invalid workspace context')
30
+ }
31
+
32
+ // 3. Determine library path
33
+ const libraryPath = path.join(context.workspaceRoot, 'libraries', libraryName)
34
+
35
+ // 4. Check if library exists
36
+ if (!(await fs.pathExists(libraryPath))) {
37
+ throw new LibraryNotFoundError(libraryName, libraryPath)
38
+ }
39
+
40
+ // 5. Delete the library directory
41
+ await fs.remove(libraryPath)
42
+
43
+ // 6. Update dependencies
44
+ await npm.install(context.workspaceRoot)
45
+
46
+ return {
47
+ libraryName,
48
+ }
49
+ }
50
+ }
@@ -0,0 +1,7 @@
1
+ export interface DeleteLibraryRequest {
2
+ libraryName: string
3
+ }
4
+
5
+ export interface DeleteLibraryResult {
6
+ libraryName: string
7
+ }
@@ -0,0 +1,128 @@
1
+ /* eslint-disable no-console */
2
+ import * as path from 'path'
3
+
4
+ import chalk from 'chalk'
5
+ import { Command } from 'commander'
6
+ import inquirer from 'inquirer'
7
+ import ora from 'ora'
8
+
9
+ import { detectLaunch77Context } from '@launch77/plugin-runtime'
10
+ import { PluginService } from '../services/plugin-svc.js'
11
+
12
+ export function pluginDeleteCommand(): Command {
13
+ const command = new Command('plugin:delete')
14
+ .argument('<plugin-name>', 'Name of the plugin to delete')
15
+ .description('Delete a plugin from the workspace (requires confirmation)')
16
+ .action(async (pluginName: string) => {
17
+ try {
18
+ console.log(chalk.yellow(`\n⚠️ WARNING: You are about to delete the plugin '${pluginName}'\n`))
19
+
20
+ // Detect context
21
+ const context = await detectLaunch77Context(process.cwd())
22
+
23
+ // Create plugin service
24
+ const pluginService = new PluginService()
25
+
26
+ // Get plugin path for display (before deletion)
27
+ const pluginPath = path.join(context.workspaceRoot, 'plugins', pluginName)
28
+
29
+ // Display what will be deleted
30
+ console.log(chalk.red('Plugin location:'), pluginPath)
31
+ console.log(chalk.red('\nThis will permanently delete:'))
32
+ console.log(chalk.gray(' • All source code and configuration files'))
33
+ console.log(chalk.gray(' • All node_modules (will be cleaned from monorepo)'))
34
+ console.log(chalk.gray(' • All build artifacts'))
35
+ console.log(chalk.gray(' • Plugin templates and generators'))
36
+
37
+ console.log(chalk.yellow('\n⚠️ Important notes:'))
38
+ console.log(chalk.gray(' • Check if plugin is installed in other packages'))
39
+ console.log(chalk.gray(' • If published to npm, you may want to deprecate/unpublish it manually'))
40
+ console.log(chalk.gray(' • Remove plugin from launch77.installedPlugins in dependent packages manually'))
41
+
42
+ console.log(chalk.red('\n⚠️ THIS ACTION CANNOT BE UNDONE!\n'))
43
+
44
+ // First confirmation: Type plugin name
45
+ const { confirmName } = await inquirer.prompt([
46
+ {
47
+ type: 'input',
48
+ name: 'confirmName',
49
+ message: `Type the plugin name to confirm:`,
50
+ validate: (input: string) => {
51
+ if (input === pluginName) {
52
+ return true
53
+ }
54
+ return `Please type '${pluginName}' exactly to confirm`
55
+ },
56
+ },
57
+ ])
58
+
59
+ if (confirmName !== pluginName) {
60
+ console.log(chalk.gray('\n✖ Deletion cancelled\n'))
61
+ return
62
+ }
63
+
64
+ // Second confirmation: Type DELETE
65
+ const { confirmDelete } = await inquirer.prompt([
66
+ {
67
+ type: 'input',
68
+ name: 'confirmDelete',
69
+ message: 'Type DELETE (all caps) to confirm permanent deletion:',
70
+ validate: (input: string) => {
71
+ if (input === 'DELETE') {
72
+ return true
73
+ }
74
+ return 'Please type DELETE (all caps) to confirm'
75
+ },
76
+ },
77
+ ])
78
+
79
+ if (confirmDelete !== 'DELETE') {
80
+ console.log(chalk.gray('\n✖ Deletion cancelled\n'))
81
+ return
82
+ }
83
+
84
+ // Delete the plugin
85
+ const deleteSpinner = ora('Deleting plugin directory...').start()
86
+ try {
87
+ await pluginService.deletePlugin({ pluginName }, context)
88
+ deleteSpinner.succeed('Plugin directory deleted')
89
+ } catch (error) {
90
+ deleteSpinner.fail('Failed to delete plugin directory')
91
+ throw error
92
+ }
93
+
94
+ // Update dependencies
95
+ const installSpinner = ora('Updating workspace dependencies...').start()
96
+ try {
97
+ // This is already done by plugin service, just show spinner for consistency
98
+ installSpinner.succeed('Dependencies updated (package-lock.json cleaned)')
99
+ } catch (error) {
100
+ installSpinner.fail('Failed to update dependencies')
101
+ console.log(chalk.yellow('\n⚠️ You may need to run "npm install" manually at the workspace root\n'))
102
+ }
103
+
104
+ // Success message
105
+ console.log(chalk.green(`\n✅ Plugin '${pluginName}' has been deleted\n`))
106
+
107
+ // Reminder about cleanup
108
+ console.log(chalk.yellow('📝 Remember to:'))
109
+ console.log(chalk.gray(' • Check packages that had this plugin installed'))
110
+ console.log(chalk.gray(' • Remove plugin from launch77.installedPlugins in package.json files'))
111
+ console.log(chalk.gray(' • Remove any plugin-generated files if needed'))
112
+ console.log(chalk.gray(' • Consider deprecating/unpublishing from npm if published'))
113
+ console.log()
114
+
115
+ // Reminder about Git
116
+ console.log(chalk.yellow('📝 Remember to commit the deletion:'))
117
+ console.log(chalk.gray(' git add -A'))
118
+ console.log(chalk.gray(` git commit -m "Remove ${pluginName} plugin"`))
119
+ console.log()
120
+ } catch (error) {
121
+ const message = error instanceof Error ? error.message : String(error)
122
+ console.error(chalk.red('Error:'), message)
123
+ process.exit(1)
124
+ }
125
+ })
126
+
127
+ return command
128
+ }
@@ -123,3 +123,23 @@ ${cause ? `\nOriginal error: ${cause.message}` : ''}`)
123
123
  this.name = 'NpmInstallationError'
124
124
  }
125
125
  }
126
+
127
+ /**
128
+ * Error when plugin directory is not found for deletion
129
+ */
130
+ export class PluginDirectoryNotFoundError extends Error {
131
+ constructor(pluginName: string, expectedPath: string) {
132
+ super(`Plugin '${pluginName}' does not exist.\n\n` + `Expected location: ${expectedPath}\n\n` + `Available plugins:\n` + ` cd plugins/\n` + ` ls`)
133
+ this.name = 'PluginDirectoryNotFoundError'
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Error when plugin name validation fails
139
+ */
140
+ export class InvalidPluginNameError extends Error {
141
+ constructor(message: string) {
142
+ super(message)
143
+ this.name = 'InvalidPluginNameError'
144
+ }
145
+ }
@@ -11,6 +11,7 @@ export { PluginNotFoundError, InvalidPluginContextError, PluginInstallationError
11
11
  // Commands
12
12
  export { pluginInstallCommand } from './commands/plugin-install.js'
13
13
  export { pluginCreateCommand } from './commands/plugin-create.js'
14
+ export { pluginDeleteCommand } from './commands/delete-plugin.js'
14
15
 
15
16
  // Utilities
16
17
  export { listAvailablePlugins } from './lib/plugin-registry.js'
@@ -120,6 +120,7 @@ describe('Plugin Resolver', () => {
120
120
  await fs.ensureDir(pluginPath)
121
121
  await fs.ensureDir(path.join(pluginPath, 'dist'))
122
122
  await fs.writeFile(path.join(pluginPath, 'plugin.json'), JSON.stringify({ name: 'my-plugin', version: '1.0.0' }))
123
+ await fs.writeFile(path.join(pluginPath, 'package.json'), JSON.stringify({ name: 'my-plugin', version: '0.0.1' }))
123
124
  await fs.writeFile(path.join(pluginPath, 'dist/generator.js'), 'console.log("test")')
124
125
 
125
126
  const result = await resolver.resolveLocation('my-plugin', tempDir)
@@ -128,6 +129,7 @@ describe('Plugin Resolver', () => {
128
129
  source: 'local',
129
130
  resolvedName: 'my-plugin',
130
131
  localPath: pluginPath,
132
+ version: '0.0.1',
131
133
  })
132
134
  })
133
135
 
@@ -21,10 +21,21 @@ export class PluginResolver extends PackageResolver {
21
21
  }
22
22
 
23
23
  protected async verify(localPath: string): Promise<boolean> {
24
- // Verify it's a valid plugin (has plugin.json and dist/generator.js)
24
+ // Verify it's a valid plugin (has plugin.json, dist/generator.js, and package.json with version)
25
25
  const hasPluginJson = await fs.pathExists(path.join(localPath, 'plugin.json'))
26
26
  const hasGenerator = await fs.pathExists(path.join(localPath, 'dist/generator.js'))
27
+ const hasPackageJson = await fs.pathExists(path.join(localPath, 'package.json'))
27
28
 
28
- return hasPluginJson && hasGenerator
29
+ if (!hasPluginJson || !hasGenerator || !hasPackageJson) {
30
+ return false
31
+ }
32
+
33
+ // Verify package.json has a version field
34
+ try {
35
+ const packageJson = await fs.readJson(path.join(localPath, 'package.json'))
36
+ return !!packageJson.version
37
+ } catch {
38
+ return false
39
+ }
29
40
  }
30
41
  }