@girardmedia/bootspring 2.0.21 → 2.0.23

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 (159) hide show
  1. package/bin/bootspring.js +5 -0
  2. package/cli/org.js +474 -0
  3. package/cli/preseed/index.js +16 -0
  4. package/cli/preseed/interactive.js +143 -0
  5. package/cli/preseed/templates.js +227 -0
  6. package/cli/preseed.js +9 -301
  7. package/cli/seed/builders/ai-context-builder.js +85 -0
  8. package/cli/seed/builders/index.js +13 -0
  9. package/cli/seed/builders/seed-builder.js +272 -0
  10. package/cli/seed/extractors/content-extractors.js +383 -0
  11. package/cli/seed/extractors/index.js +47 -0
  12. package/cli/seed/extractors/metadata-extractors.js +167 -0
  13. package/cli/seed/extractors/section-extractor.js +54 -0
  14. package/cli/seed/extractors/stack-extractors.js +228 -0
  15. package/cli/seed/index.js +18 -0
  16. package/cli/seed/utils/folder-structure.js +84 -0
  17. package/cli/seed/utils/index.js +11 -0
  18. package/cli/seed.js +23 -1074
  19. package/core/api-client.js +77 -0
  20. package/core/entitlements.js +36 -0
  21. package/core/organizations.js +223 -0
  22. package/core/policies.js +51 -6
  23. package/core/policy-matrix.js +303 -0
  24. package/core/project-context.js +1 -0
  25. package/dist/cli/index.d.ts +3 -0
  26. package/dist/cli/index.js +3220 -0
  27. package/dist/cli/index.js.map +1 -0
  28. package/dist/context-McpJQa_2.d.ts +5710 -0
  29. package/dist/core/index.d.ts +635 -0
  30. package/dist/core/index.js +2593 -0
  31. package/dist/core/index.js.map +1 -0
  32. package/dist/index-QqbeEiDm.d.ts +857 -0
  33. package/dist/index-UiYCgwiH.d.ts +174 -0
  34. package/dist/index.d.ts +453 -0
  35. package/dist/index.js +44228 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/mcp/index.d.ts +1 -0
  38. package/dist/mcp/index.js +41173 -0
  39. package/dist/mcp/index.js.map +1 -0
  40. package/generators/index.ts +82 -0
  41. package/intelligence/orchestrator/config/failure-signatures.js +48 -0
  42. package/intelligence/orchestrator/config/index.js +23 -0
  43. package/intelligence/orchestrator/config/pack-lifecycle.js +262 -0
  44. package/intelligence/orchestrator/config/phases.js +111 -0
  45. package/intelligence/orchestrator/config/remediation.js +150 -0
  46. package/intelligence/orchestrator/config/workflows.js +168 -0
  47. package/intelligence/orchestrator/core/index.js +16 -0
  48. package/intelligence/orchestrator/core/state-manager.js +88 -0
  49. package/intelligence/orchestrator/core/telemetry.js +24 -0
  50. package/intelligence/orchestrator/index.js +17 -0
  51. package/intelligence/orchestrator.js +17 -512
  52. package/mcp/contracts/mcp-contract.v1.json +1 -1
  53. package/package.json +16 -3
  54. package/src/cli/agent.ts +703 -0
  55. package/src/cli/analyze.ts +640 -0
  56. package/src/cli/audit.ts +707 -0
  57. package/src/cli/auth.ts +930 -0
  58. package/src/cli/billing.ts +364 -0
  59. package/src/cli/build.ts +1089 -0
  60. package/src/cli/business.ts +508 -0
  61. package/src/cli/checkpoint-utils.ts +236 -0
  62. package/src/cli/checkpoint.ts +757 -0
  63. package/src/cli/cloud-sync.ts +534 -0
  64. package/src/cli/content.ts +273 -0
  65. package/src/cli/context.ts +667 -0
  66. package/src/cli/dashboard.ts +133 -0
  67. package/src/cli/deploy.ts +704 -0
  68. package/src/cli/doctor.ts +480 -0
  69. package/src/cli/fundraise.ts +494 -0
  70. package/src/cli/generate.ts +346 -0
  71. package/src/cli/github-cmd.ts +566 -0
  72. package/src/cli/health.ts +599 -0
  73. package/src/cli/index.ts +113 -0
  74. package/src/cli/init.ts +838 -0
  75. package/src/cli/legal.ts +495 -0
  76. package/src/cli/log.ts +316 -0
  77. package/src/cli/loop.ts +1660 -0
  78. package/src/cli/manager.ts +878 -0
  79. package/src/cli/mcp.ts +275 -0
  80. package/src/cli/memory.ts +346 -0
  81. package/src/cli/metrics.ts +590 -0
  82. package/src/cli/monitor.ts +960 -0
  83. package/src/cli/mvp.ts +662 -0
  84. package/src/cli/onboard.ts +663 -0
  85. package/src/cli/orchestrator.ts +622 -0
  86. package/src/cli/plugin.ts +483 -0
  87. package/src/cli/prd.ts +671 -0
  88. package/src/cli/preseed-start.ts +1633 -0
  89. package/src/cli/preseed.ts +2434 -0
  90. package/src/cli/project.ts +526 -0
  91. package/src/cli/quality.ts +885 -0
  92. package/src/cli/security.ts +1079 -0
  93. package/src/cli/seed.ts +1224 -0
  94. package/src/cli/skill.ts +537 -0
  95. package/src/cli/suggest.ts +1225 -0
  96. package/src/cli/switch.ts +518 -0
  97. package/src/cli/task.ts +780 -0
  98. package/src/cli/telemetry.ts +172 -0
  99. package/src/cli/todo.ts +627 -0
  100. package/src/cli/types.ts +15 -0
  101. package/src/cli/update.ts +334 -0
  102. package/src/cli/visualize.ts +609 -0
  103. package/src/cli/watch.ts +895 -0
  104. package/src/cli/workspace.ts +709 -0
  105. package/src/core/action-recorder.ts +673 -0
  106. package/src/core/analyze-workflow.ts +1453 -0
  107. package/src/core/api-client.ts +1120 -0
  108. package/src/core/audit-workflow.ts +1681 -0
  109. package/src/core/auth.ts +471 -0
  110. package/src/core/build-orchestrator.ts +509 -0
  111. package/src/core/build-state.ts +621 -0
  112. package/src/core/checkpoint-engine.ts +482 -0
  113. package/src/core/config.ts +1285 -0
  114. package/src/core/context-loader.ts +694 -0
  115. package/src/core/context.ts +410 -0
  116. package/src/core/deploy-workflow.ts +1085 -0
  117. package/src/core/entitlements.ts +322 -0
  118. package/src/core/github-sync.ts +720 -0
  119. package/src/core/index.ts +981 -0
  120. package/src/core/ingest.ts +1186 -0
  121. package/src/core/metrics-engine.ts +886 -0
  122. package/src/core/mvp.ts +847 -0
  123. package/src/core/onboard-workflow.ts +1293 -0
  124. package/src/core/policies.ts +81 -0
  125. package/src/core/preseed-workflow.ts +1163 -0
  126. package/src/core/preseed.ts +1826 -0
  127. package/src/core/project-context.ts +380 -0
  128. package/src/core/project-state.ts +699 -0
  129. package/src/core/r2-sync.ts +691 -0
  130. package/src/core/scaffold.ts +1715 -0
  131. package/src/core/session.ts +286 -0
  132. package/src/core/task-extractor.ts +799 -0
  133. package/src/core/telemetry.ts +371 -0
  134. package/src/core/tier-enforcement.ts +737 -0
  135. package/src/core/utils.ts +437 -0
  136. package/src/index.ts +29 -0
  137. package/src/intelligence/agent-collab.ts +2376 -0
  138. package/src/intelligence/auto-suggest.ts +713 -0
  139. package/src/intelligence/content-gen.ts +1351 -0
  140. package/src/intelligence/cross-project.ts +1692 -0
  141. package/src/intelligence/git-memory.ts +529 -0
  142. package/src/intelligence/index.ts +318 -0
  143. package/src/intelligence/orchestrator.ts +534 -0
  144. package/src/intelligence/prd.ts +466 -0
  145. package/src/intelligence/recommendations.ts +982 -0
  146. package/src/intelligence/workflow-composer.ts +1472 -0
  147. package/src/mcp/capabilities.ts +233 -0
  148. package/src/mcp/index.ts +37 -0
  149. package/src/mcp/registry.ts +1268 -0
  150. package/src/mcp/response-formatter.ts +797 -0
  151. package/src/mcp/server.ts +240 -0
  152. package/src/types/agent.ts +69 -0
  153. package/src/types/config.ts +86 -0
  154. package/src/types/context.ts +77 -0
  155. package/src/types/index.ts +53 -0
  156. package/src/types/mcp.ts +91 -0
  157. package/src/types/skills.ts +47 -0
  158. package/src/types/workflow.ts +155 -0
  159. package/generators/index.js +0 -18
@@ -0,0 +1,704 @@
1
+ /**
2
+ * Bootspring Deploy CLI
3
+ *
4
+ * Deploy projects to various hosting platforms with
5
+ * pre-deployment validation and quality checks.
6
+ *
7
+ * @package bootspring
8
+ * @module cli/deploy
9
+ */
10
+
11
+ import * as path from 'path';
12
+ import * as fs from 'fs';
13
+
14
+ // Type interfaces for JS modules
15
+ interface Colors {
16
+ reset: string;
17
+ bold: string;
18
+ dim: string;
19
+ cyan: string;
20
+ green: string;
21
+ yellow: string;
22
+ red: string;
23
+ }
24
+
25
+ interface PrintModule {
26
+ header(msg: string): void;
27
+ info(msg: string): void;
28
+ success(msg: string): void;
29
+ warning(msg: string): void;
30
+ error(msg: string): void;
31
+ }
32
+
33
+ interface UtilsModule {
34
+ COLORS: Colors;
35
+ print: PrintModule;
36
+ parseArgs(args: string[]): ParsedArgs;
37
+ createSpinner(text: string): Spinner;
38
+ }
39
+
40
+ interface ParsedArgs {
41
+ _: string[];
42
+ preview?: boolean | undefined;
43
+ 'skip-quality'?: boolean | undefined;
44
+ 'dry-run'?: boolean | undefined;
45
+ force?: boolean | undefined;
46
+ }
47
+
48
+ interface Spinner {
49
+ start(): Spinner;
50
+ stop(): void;
51
+ succeed(text: string): void;
52
+ fail(text: string): void;
53
+ warn(text: string): void;
54
+ info(text: string): void;
55
+ text: string;
56
+ }
57
+
58
+ interface DeployPhase {
59
+ name: string;
60
+ required: boolean;
61
+ }
62
+
63
+ interface DeployTarget {
64
+ id: string;
65
+ name: string;
66
+ description: string;
67
+ cli: string;
68
+ installCmd: string;
69
+ deployCmd: string;
70
+ }
71
+
72
+ interface DeployProgress {
73
+ isComplete: boolean;
74
+ overall: {
75
+ percentage: number;
76
+ };
77
+ target?: string | undefined;
78
+ startedAt?: string | undefined;
79
+ phases: Array<{
80
+ name: string;
81
+ status: string;
82
+ required: boolean;
83
+ }>;
84
+ }
85
+
86
+ interface ValidationResult {
87
+ passed: boolean;
88
+ checks: Record<string, boolean>;
89
+ issues: string[];
90
+ warnings: string[];
91
+ }
92
+
93
+ interface TargetDetectionResult {
94
+ selected?: string | undefined;
95
+ detected: Array<{ id: string; name: string }>;
96
+ available: Array<{ name: string }>;
97
+ framework?: string | undefined;
98
+ recommendations: Array<{ id: string }>;
99
+ }
100
+
101
+ interface BuildResult {
102
+ dryRun?: boolean | undefined;
103
+ }
104
+
105
+ interface QualityResult {
106
+ passed: boolean;
107
+ skipped: boolean;
108
+ checks: Record<string, { passed: boolean | null }>;
109
+ }
110
+
111
+ interface DeployResult {
112
+ target: string;
113
+ url?: string | undefined;
114
+ dryRun?: boolean | undefined;
115
+ success?: boolean | undefined;
116
+ }
117
+
118
+ interface VerifyResult {
119
+ passed: boolean;
120
+ skipped: boolean;
121
+ statusCode?: number | undefined;
122
+ dryRun?: boolean | undefined;
123
+ }
124
+
125
+ interface GeneratedConfig {
126
+ file: string;
127
+ content: string;
128
+ }
129
+
130
+ interface PhaseData {
131
+ result?: DeployResult | undefined;
132
+ }
133
+
134
+ interface WorkflowState {
135
+ target: string;
136
+ phases: Record<string, PhaseData>;
137
+ }
138
+
139
+ interface DeployWorkflowEngineInstance {
140
+ hasWorkflow(): boolean;
141
+ loadState(): void;
142
+ getProgress(): DeployProgress;
143
+ initializeWorkflow(target: string | null): void;
144
+ getNextPhase(): string | null;
145
+ startPhase(phaseId: string): void;
146
+ completePhase(phaseId: string, result: unknown): void;
147
+ failPhase(phaseId: string, message: string): void;
148
+ skipPhase(phaseId: string): void;
149
+ runValidation(): Promise<ValidationResult>;
150
+ detectTarget(): Promise<TargetDetectionResult>;
151
+ setTarget(targetId: string): void;
152
+ runBuild(): Promise<BuildResult>;
153
+ runQualityChecks(): Promise<QualityResult>;
154
+ runDeploy(): Promise<DeployResult>;
155
+ runVerification(): Promise<VerifyResult>;
156
+ getTargetInfo(targetId: string): DeployTarget;
157
+ getAllTargets(): DeployTarget[];
158
+ checkCliInstalled(cli: string): boolean;
159
+ generateTargetConfig(targetId: string): GeneratedConfig | null;
160
+ resetWorkflow(): void;
161
+ state: WorkflowState;
162
+ }
163
+
164
+ interface DeployWorkflowEngineClass {
165
+ new (projectRoot: string, options?: { dryRun?: boolean; skipQuality?: boolean; env?: string }): DeployWorkflowEngineInstance;
166
+ }
167
+
168
+ interface DeployWorkflowModule {
169
+ DeployWorkflowEngine: DeployWorkflowEngineClass;
170
+ DEPLOY_TARGETS: Record<string, DeployTarget>;
171
+ DEPLOY_PHASES: Record<string, DeployPhase>;
172
+ }
173
+
174
+ const utils = require('../core/utils') as UtilsModule;
175
+ const { DeployWorkflowEngine, DEPLOY_TARGETS, DEPLOY_PHASES } = require('../core/deploy-workflow') as DeployWorkflowModule;
176
+
177
+ // Get project root
178
+ const projectRoot = process.cwd();
179
+
180
+ /**
181
+ * Show deploy help
182
+ */
183
+ function showHelp(): void {
184
+ console.log(`
185
+ ${utils.COLORS.bold}Bootspring Deploy${utils.COLORS.reset}
186
+ Deploy your project to production
187
+
188
+ ${utils.COLORS.bold}Usage:${utils.COLORS.reset}
189
+ bootspring deploy Deploy to detected/configured target
190
+ bootspring deploy <target> Deploy to specific target
191
+ bootspring deploy status Show deployment status
192
+ bootspring deploy targets List available targets
193
+ bootspring deploy init <target> Initialize deployment config
194
+ bootspring deploy reset Reset deployment state
195
+
196
+ ${utils.COLORS.bold}Targets:${utils.COLORS.reset}
197
+ vercel Vercel (recommended for Next.js)
198
+ railway Railway (full-stack apps)
199
+ fly Fly.io (edge deployment)
200
+ netlify Netlify (static sites & serverless)
201
+ docker Docker (build image)
202
+ aws AWS Amplify
203
+
204
+ ${utils.COLORS.bold}Options:${utils.COLORS.reset}
205
+ --preview Preview deployment (not production)
206
+ --skip-quality Skip quality checks
207
+ --dry-run Show what would be deployed
208
+ --force Force deployment even with warnings
209
+
210
+ ${utils.COLORS.bold}Examples:${utils.COLORS.reset}
211
+ bootspring deploy # Auto-detect and deploy
212
+ bootspring deploy vercel # Deploy to Vercel
213
+ bootspring deploy --preview # Deploy preview
214
+ bootspring deploy init vercel # Create vercel.json
215
+ bootspring deploy targets # List all targets
216
+ `);
217
+ }
218
+
219
+ /**
220
+ * Start deployment workflow
221
+ */
222
+ async function deployStart(args: ParsedArgs): Promise<void> {
223
+ const target = args._[0] || null;
224
+ const preview = args.preview || false;
225
+ const skipQuality = args['skip-quality'] || false;
226
+ const dryRun = args['dry-run'] || false;
227
+ const force = args.force || false;
228
+
229
+ const workflow = new DeployWorkflowEngine(projectRoot, {
230
+ dryRun,
231
+ skipQuality,
232
+ env: preview ? 'preview' : 'production'
233
+ });
234
+
235
+ // Check for existing workflow
236
+ if (workflow.hasWorkflow()) {
237
+ workflow.loadState();
238
+ const progress = workflow.getProgress();
239
+
240
+ if (!progress.isComplete) {
241
+ utils.print.info('Existing deployment in progress.');
242
+ console.log(` Progress: ${progress.overall.percentage}% complete`);
243
+ console.log(` Target: ${progress.target || 'not set'}`);
244
+ console.log('');
245
+ utils.print.info('Use "bootspring deploy reset" to start fresh');
246
+ return;
247
+ }
248
+ }
249
+
250
+ // Initialize workflow
251
+ utils.print.header('Bootspring Deploy');
252
+ console.log(preview ? 'Preview deployment' : 'Production deployment');
253
+ if (dryRun) {
254
+ console.log(`${utils.COLORS.yellow}[DRY RUN]${utils.COLORS.reset}`);
255
+ }
256
+ console.log('');
257
+
258
+ workflow.initializeWorkflow(target);
259
+
260
+ // Run workflow
261
+ await runWorkflowLoop(workflow, { force, dryRun });
262
+ }
263
+
264
+ /**
265
+ * Run the workflow loop
266
+ */
267
+ async function runWorkflowLoop(workflow: DeployWorkflowEngineInstance, options: { force?: boolean; dryRun?: boolean } = {}): Promise<void> {
268
+ while (true) {
269
+ const nextPhaseId = workflow.getNextPhase();
270
+
271
+ if (!nextPhaseId) {
272
+ break;
273
+ }
274
+
275
+ const phase = DEPLOY_PHASES[nextPhaseId];
276
+
277
+ // Run the phase
278
+ workflow.startPhase(nextPhaseId);
279
+ const spinner = utils.createSpinner(`${phase?.name || nextPhaseId}...`).start();
280
+
281
+ try {
282
+ let result: unknown;
283
+
284
+ switch (nextPhaseId) {
285
+ case 'validate':
286
+ result = await workflow.runValidation();
287
+ if ((result as ValidationResult).passed) {
288
+ spinner.succeed('Pre-flight validation passed');
289
+ } else {
290
+ spinner.fail('Pre-flight validation failed');
291
+ displayValidationResults(result as ValidationResult);
292
+ if (!options.force) {
293
+ utils.print.error('Fix issues above or use --force to continue');
294
+ workflow.failPhase(nextPhaseId, 'Validation failed');
295
+ return;
296
+ }
297
+ utils.print.warning('Continuing with --force');
298
+ }
299
+ displayValidationResults(result as ValidationResult);
300
+ break;
301
+
302
+ case 'target':
303
+ result = await workflow.detectTarget();
304
+ if ((result as TargetDetectionResult).selected) {
305
+ const target = workflow.getTargetInfo((result as TargetDetectionResult).selected as string);
306
+ spinner.succeed(`Target: ${target.name}`);
307
+ } else if ((result as TargetDetectionResult).detected.length > 0) {
308
+ const detected = (result as TargetDetectionResult).detected;
309
+ spinner.succeed(`Detected: ${detected.map(t => t.name).join(', ')}`);
310
+ // Auto-select first detected
311
+ workflow.setTarget(detected[0]?.id || '');
312
+ (result as TargetDetectionResult).selected = detected[0]?.id;
313
+ } else {
314
+ spinner.fail('No deployment target detected');
315
+ displayTargetSelection(result as TargetDetectionResult);
316
+ utils.print.error('Run "bootspring deploy init <target>" to configure');
317
+ workflow.failPhase(nextPhaseId, 'No target');
318
+ return;
319
+ }
320
+ displayTargetResults(result as TargetDetectionResult);
321
+ break;
322
+
323
+ case 'build':
324
+ if (options.dryRun) {
325
+ spinner.info('Build: [DRY RUN] Would run npm run build');
326
+ result = { dryRun: true };
327
+ } else {
328
+ spinner.text = 'Building...';
329
+ result = await workflow.runBuild();
330
+ spinner.succeed('Build successful');
331
+ }
332
+ break;
333
+
334
+ case 'quality':
335
+ result = await workflow.runQualityChecks();
336
+ if ((result as QualityResult).skipped) {
337
+ spinner.info('Quality checks skipped');
338
+ } else if ((result as QualityResult).passed) {
339
+ spinner.succeed('Quality checks passed');
340
+ } else {
341
+ spinner.warn('Some quality checks failed');
342
+ }
343
+ displayQualityResults(result as QualityResult);
344
+ break;
345
+
346
+ case 'deploy':
347
+ if (options.dryRun) {
348
+ const target = workflow.getTargetInfo(workflow.state.target);
349
+ spinner.info(`Deploy: [DRY RUN] Would run: ${target.deployCmd}`);
350
+ result = { dryRun: true };
351
+ } else {
352
+ spinner.text = 'Deploying...';
353
+ console.log('');
354
+ result = await workflow.runDeploy();
355
+ console.log('');
356
+ spinner.succeed(`Deployed to ${(result as DeployResult).target}`);
357
+ if ((result as DeployResult).url) {
358
+ console.log(` ${utils.COLORS.green}URL:${utils.COLORS.reset} ${(result as DeployResult).url}`);
359
+ }
360
+ }
361
+ break;
362
+
363
+ case 'verify':
364
+ if (options.dryRun) {
365
+ spinner.info('Verification: [DRY RUN] Skipped');
366
+ result = { dryRun: true };
367
+ } else {
368
+ result = await workflow.runVerification();
369
+ if ((result as VerifyResult).skipped) {
370
+ spinner.info('Verification skipped');
371
+ } else if ((result as VerifyResult).passed) {
372
+ spinner.succeed(`Verification passed (HTTP ${(result as VerifyResult).statusCode})`);
373
+ } else {
374
+ spinner.warn('Verification failed');
375
+ }
376
+ }
377
+ break;
378
+
379
+ default:
380
+ spinner.warn(`Unknown phase: ${nextPhaseId}`);
381
+ workflow.skipPhase(nextPhaseId);
382
+ continue;
383
+ }
384
+
385
+ workflow.completePhase(nextPhaseId, result);
386
+
387
+ } catch (error) {
388
+ spinner.fail(`Failed: ${(error as Error).message}`);
389
+ workflow.failPhase(nextPhaseId, (error as Error).message);
390
+
391
+ if (phase?.required) {
392
+ utils.print.error('Deployment failed.');
393
+ return;
394
+ }
395
+ }
396
+
397
+ console.log('');
398
+ }
399
+
400
+ // Deployment complete
401
+ displayCompletionSummary(workflow, options);
402
+ }
403
+
404
+ /**
405
+ * Display validation results
406
+ */
407
+ function displayValidationResults(result: ValidationResult): void {
408
+ console.log('');
409
+
410
+ for (const [check, passed] of Object.entries(result.checks)) {
411
+ const icon = passed ?
412
+ `${utils.COLORS.green}✓${utils.COLORS.reset}` :
413
+ `${utils.COLORS.yellow}○${utils.COLORS.reset}`;
414
+ console.log(` ${icon} ${formatCheckName(check)}`);
415
+ }
416
+
417
+ if (result.issues.length > 0) {
418
+ console.log('');
419
+ console.log(` ${utils.COLORS.red}Issues:${utils.COLORS.reset}`);
420
+ for (const issue of result.issues) {
421
+ console.log(` - ${issue}`);
422
+ }
423
+ }
424
+
425
+ if (result.warnings.length > 0) {
426
+ console.log('');
427
+ console.log(` ${utils.COLORS.yellow}Warnings:${utils.COLORS.reset}`);
428
+ for (const warning of result.warnings) {
429
+ console.log(` - ${warning}`);
430
+ }
431
+ }
432
+ }
433
+
434
+ /**
435
+ * Display target results
436
+ */
437
+ function displayTargetResults(result: TargetDetectionResult): void {
438
+ if (result.framework) {
439
+ console.log(` ${utils.COLORS.dim}Framework: ${result.framework}${utils.COLORS.reset}`);
440
+ }
441
+
442
+ if (result.available.length > 0 && !result.selected) {
443
+ console.log('');
444
+ console.log(' Available CLIs:');
445
+ for (const target of result.available.slice(0, 3)) {
446
+ console.log(` ${utils.COLORS.cyan}${target.name}${utils.COLORS.reset}`);
447
+ }
448
+ }
449
+ }
450
+
451
+ /**
452
+ * Display target selection options
453
+ */
454
+ function displayTargetSelection(result: TargetDetectionResult): void {
455
+ console.log('');
456
+ console.log('Recommended targets for your project:');
457
+
458
+ for (const rec of result.recommendations.slice(0, 3)) {
459
+ const target = DEPLOY_TARGETS[rec.id];
460
+ if (target) {
461
+ console.log(` ${utils.COLORS.cyan}${rec.id}${utils.COLORS.reset} - ${target.description}`);
462
+ }
463
+ }
464
+
465
+ console.log('');
466
+ console.log('Run: bootspring deploy init <target>');
467
+ }
468
+
469
+ /**
470
+ * Display quality results
471
+ */
472
+ function displayQualityResults(result: QualityResult): void {
473
+ if (result.skipped) return;
474
+
475
+ console.log('');
476
+ for (const [check, status] of Object.entries(result.checks)) {
477
+ if (status.passed === null) continue;
478
+
479
+ const icon = status.passed ?
480
+ `${utils.COLORS.green}✓${utils.COLORS.reset}` :
481
+ `${utils.COLORS.yellow}○${utils.COLORS.reset}`;
482
+ console.log(` ${icon} ${check}`);
483
+ }
484
+ }
485
+
486
+ /**
487
+ * Display completion summary
488
+ */
489
+ function displayCompletionSummary(workflow: DeployWorkflowEngineInstance, options: { dryRun?: boolean }): void {
490
+ const deployResult = workflow.state.phases.deploy?.result;
491
+
492
+ console.log('');
493
+
494
+ if (options.dryRun) {
495
+ utils.print.info('Dry run complete - no changes made');
496
+ return;
497
+ }
498
+
499
+ if (deployResult?.url) {
500
+ utils.print.success('Deployment successful!');
501
+ console.log('');
502
+ console.log(` ${utils.COLORS.bold}URL:${utils.COLORS.reset} ${deployResult.url}`);
503
+ } else if (deployResult?.success) {
504
+ utils.print.success('Deployment successful!');
505
+ } else {
506
+ utils.print.warning('Deployment completed with warnings');
507
+ }
508
+
509
+ console.log('');
510
+ utils.print.info('Next steps:');
511
+ console.log(' 1. Verify your deployment is working');
512
+ console.log(' 2. Set up environment variables if needed');
513
+ console.log(' 3. Configure custom domain (optional)');
514
+ }
515
+
516
+ /**
517
+ * Format check name
518
+ */
519
+ function formatCheckName(name: string): string {
520
+ const names: Record<string, string> = {
521
+ packageJson: 'package.json exists',
522
+ nodeModules: 'Dependencies installed',
523
+ buildScript: 'Build script available',
524
+ gitClean: 'Git working tree clean',
525
+ envFile: 'Environment file exists'
526
+ };
527
+ return names[name] || name;
528
+ }
529
+
530
+ /**
531
+ * Show deployment status
532
+ */
533
+ async function deployStatus(): Promise<void> {
534
+ const workflow = new DeployWorkflowEngine(projectRoot);
535
+
536
+ if (!workflow.hasWorkflow()) {
537
+ utils.print.info('No deployment workflow started.');
538
+ utils.print.info('Run "bootspring deploy" to begin.');
539
+ return;
540
+ }
541
+
542
+ workflow.loadState();
543
+ const progress = workflow.getProgress();
544
+
545
+ utils.print.header('Deployment Status');
546
+
547
+ console.log(`Progress: ${progress.overall.percentage}% complete`);
548
+ console.log(`Target: ${progress.target || 'not set'}`);
549
+ console.log(`Started: ${progress.startedAt ? new Date(progress.startedAt).toLocaleString() : 'N/A'}`);
550
+ console.log('');
551
+
552
+ console.log('Phases:');
553
+ for (const phase of progress.phases) {
554
+ const statusIcon = getStatusIcon(phase.status);
555
+ const required = phase.required ? '' : ' (optional)';
556
+ console.log(` ${statusIcon} ${phase.name}${required}`);
557
+ }
558
+
559
+ if (progress.isComplete) {
560
+ console.log('');
561
+ const deployResult = workflow.state.phases.deploy?.result;
562
+ if (deployResult?.url) {
563
+ console.log(`URL: ${deployResult.url}`);
564
+ }
565
+ }
566
+ }
567
+
568
+ /**
569
+ * List available targets
570
+ */
571
+ function listTargets(): void {
572
+ const workflow = new DeployWorkflowEngine(projectRoot);
573
+
574
+ utils.print.header('Deployment Targets');
575
+ console.log('');
576
+
577
+ const targets = workflow.getAllTargets();
578
+
579
+ for (const target of targets) {
580
+ const installed = workflow.checkCliInstalled(target.cli);
581
+ const icon = installed ?
582
+ `${utils.COLORS.green}✓${utils.COLORS.reset}` :
583
+ `${utils.COLORS.dim}○${utils.COLORS.reset}`;
584
+
585
+ console.log(` ${icon} ${utils.COLORS.cyan}${target.id}${utils.COLORS.reset}`);
586
+ console.log(` ${utils.COLORS.dim}${target.description}${utils.COLORS.reset}`);
587
+ console.log(` CLI: ${target.cli} ${installed ? '(installed)' : '(not installed)'}`);
588
+ console.log('');
589
+ }
590
+
591
+ console.log('Install a CLI to enable deployment:');
592
+ console.log(` ${utils.COLORS.dim}npm install -g vercel${utils.COLORS.reset}`);
593
+ }
594
+
595
+ /**
596
+ * Initialize deployment config
597
+ */
598
+ async function deployInit(args: ParsedArgs): Promise<void> {
599
+ const targetId = args._[1];
600
+
601
+ if (!targetId) {
602
+ utils.print.error('Specify a target: bootspring deploy init <target>');
603
+ console.log('');
604
+ console.log('Available targets: vercel, railway, fly, netlify, docker, aws');
605
+ return;
606
+ }
607
+
608
+ const workflow = new DeployWorkflowEngine(projectRoot);
609
+ const config = workflow.generateTargetConfig(targetId);
610
+
611
+ if (!config) {
612
+ utils.print.error(`Unknown target: ${targetId}`);
613
+ return;
614
+ }
615
+
616
+ const filePath = path.join(projectRoot, config.file);
617
+
618
+ if (fs.existsSync(filePath)) {
619
+ utils.print.warning(`${config.file} already exists`);
620
+ utils.print.info('Use --force to overwrite');
621
+ return;
622
+ }
623
+
624
+ const spinner = utils.createSpinner(`Creating ${config.file}...`).start();
625
+
626
+ try {
627
+ fs.writeFileSync(filePath, config.content);
628
+ spinner.succeed(`Created ${config.file}`);
629
+
630
+ const target = workflow.getTargetInfo(targetId);
631
+ console.log('');
632
+ utils.print.info('Next steps:');
633
+ console.log(` 1. Install CLI: ${target.installCmd}`);
634
+ console.log(` 2. Login: ${target.cli} login`);
635
+ console.log(` 3. Deploy: bootspring deploy ${targetId}`);
636
+ } catch (error) {
637
+ spinner.fail(`Failed: ${(error as Error).message}`);
638
+ }
639
+ }
640
+
641
+ /**
642
+ * Reset deployment workflow
643
+ */
644
+ async function deployReset(): Promise<void> {
645
+ const workflow = new DeployWorkflowEngine(projectRoot);
646
+
647
+ if (!workflow.hasWorkflow()) {
648
+ utils.print.info('No workflow to reset.');
649
+ return;
650
+ }
651
+
652
+ workflow.resetWorkflow();
653
+ utils.print.success('Deployment workflow reset.');
654
+ }
655
+
656
+ /**
657
+ * Get status icon
658
+ */
659
+ function getStatusIcon(status: string): string {
660
+ switch (status) {
661
+ case 'completed':
662
+ return `${utils.COLORS.green}✓${utils.COLORS.reset}`;
663
+ case 'in_progress':
664
+ return `${utils.COLORS.cyan}●${utils.COLORS.reset}`;
665
+ case 'failed':
666
+ return `${utils.COLORS.red}✗${utils.COLORS.reset}`;
667
+ case 'skipped':
668
+ return `${utils.COLORS.dim}○${utils.COLORS.reset}`;
669
+ default:
670
+ return `${utils.COLORS.dim}○${utils.COLORS.reset}`;
671
+ }
672
+ }
673
+
674
+ /**
675
+ * Main entry point
676
+ */
677
+ export async function run(args: string[]): Promise<void> {
678
+ const parsedArgs = utils.parseArgs(args);
679
+ const subcommand = parsedArgs._[0];
680
+
681
+ switch (subcommand) {
682
+ case 'status':
683
+ return deployStatus();
684
+
685
+ case 'targets':
686
+ case 'list':
687
+ return listTargets();
688
+
689
+ case 'init':
690
+ return deployInit(parsedArgs);
691
+
692
+ case 'reset':
693
+ return deployReset();
694
+
695
+ case 'help':
696
+ case '--help':
697
+ case '-h':
698
+ return showHelp();
699
+
700
+ default:
701
+ // Start deployment (subcommand might be a target)
702
+ return deployStart(parsedArgs);
703
+ }
704
+ }