@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,663 @@
1
+ /**
2
+ * Bootspring Onboard CLI
3
+ *
4
+ * Layer Bootspring onto existing projects with stack detection,
5
+ * pattern scanning, and configuration generation.
6
+ *
7
+ * @package bootspring
8
+ * @module cli/onboard
9
+ */
10
+
11
+ // Import JS modules with type interfaces
12
+ interface ParsedArgs {
13
+ _: string[];
14
+ 'dry-run'?: boolean | undefined;
15
+ 'skip-optional'?: boolean | undefined;
16
+ quick?: boolean | undefined;
17
+ apply?: boolean | undefined;
18
+ }
19
+
20
+ interface PhaseConfig {
21
+ name: string;
22
+ description: string;
23
+ required: boolean;
24
+ }
25
+
26
+ interface TechInfo {
27
+ name: string;
28
+ }
29
+
30
+ interface StackInfo {
31
+ framework?: TechInfo | undefined;
32
+ language?: TechInfo | undefined;
33
+ database?: TechInfo | undefined;
34
+ hosting?: TechInfo | undefined;
35
+ auth?: TechInfo | undefined;
36
+ payments?: TechInfo | undefined;
37
+ detected: TechInfo[];
38
+ }
39
+
40
+ interface StructureStats {
41
+ totalFiles: number;
42
+ totalDirs: number;
43
+ }
44
+
45
+ interface DetectionResult {
46
+ stack: StackInfo;
47
+ structure: {
48
+ stats: StructureStats;
49
+ };
50
+ }
51
+
52
+ interface ArchitecturePattern {
53
+ name: string;
54
+ confidence: number;
55
+ }
56
+
57
+ interface PatternResult {
58
+ architecture: {
59
+ patterns: ArchitecturePattern[];
60
+ };
61
+ features?: Record<string, boolean> | undefined;
62
+ quickMode?: boolean | undefined;
63
+ }
64
+
65
+ interface DocInfo {
66
+ file: string;
67
+ }
68
+
69
+ interface DocResult {
70
+ found: DocInfo[];
71
+ missing: DocInfo[];
72
+ }
73
+
74
+ interface ConfigResult {
75
+ files: string[];
76
+ }
77
+
78
+ interface BaselineResult {
79
+ timestamp: string;
80
+ }
81
+
82
+ interface OverallProgress {
83
+ completed: number;
84
+ total: number;
85
+ percentage: number;
86
+ }
87
+
88
+ interface PhaseProgress {
89
+ name: string;
90
+ status: string;
91
+ required: boolean;
92
+ }
93
+
94
+ interface WorkflowProgress {
95
+ overall: OverallProgress;
96
+ isComplete: boolean;
97
+ currentPhase?: string | undefined;
98
+ phases: PhaseProgress[];
99
+ startedAt?: string | undefined;
100
+ lastUpdated?: string | undefined;
101
+ detection?: {
102
+ stack?: StackInfo | undefined;
103
+ } | undefined;
104
+ }
105
+
106
+ interface ResumePoint {
107
+ phaseName: string;
108
+ }
109
+
110
+ interface OnboardWorkflowEngineClass {
111
+ new (projectRoot: string): OnboardWorkflowEngine;
112
+ }
113
+
114
+ interface OnboardWorkflowEngine {
115
+ hasWorkflow(): boolean;
116
+ loadState(): void;
117
+ initializeWorkflow(): void;
118
+ getProgress(): WorkflowProgress;
119
+ getResumePoint(): ResumePoint | null;
120
+ getNextPhase(): string | null;
121
+ startPhase(phaseId: string): void;
122
+ completePhase(phaseId: string, result?: unknown): void;
123
+ failPhase(phaseId: string, error: string): void;
124
+ skipPhase(phaseId: string): void;
125
+ resetWorkflow(): void;
126
+ applyGeneratedFiles(): void;
127
+ runDetection(): Promise<DetectionResult>;
128
+ runPatternScan(): Promise<PatternResult>;
129
+ runQuickPatternScan(): Promise<PatternResult>;
130
+ runDocDiscovery(): Promise<DocResult>;
131
+ runConfigGeneration(): Promise<ConfigResult>;
132
+ runBaselineCapture(): Promise<BaselineResult>;
133
+ }
134
+
135
+ interface OnboardWorkflowModule {
136
+ OnboardWorkflowEngine: OnboardWorkflowEngineClass;
137
+ ONBOARD_PHASES: Record<string, PhaseConfig>;
138
+ }
139
+
140
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
141
+ const utils = require('../../core/utils') as typeof import('../core/utils');
142
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
143
+ const onboardWorkflowModule = require('../../core/onboard-workflow') as OnboardWorkflowModule;
144
+
145
+ const { OnboardWorkflowEngine, ONBOARD_PHASES } = onboardWorkflowModule;
146
+
147
+ // Get project root
148
+ const projectRoot = process.cwd();
149
+
150
+ /**
151
+ * Show onboard help
152
+ */
153
+ function showHelp(): void {
154
+ console.log(`
155
+ ${utils.COLORS.bold}Bootspring Onboard${utils.COLORS.reset}
156
+ Layer Bootspring onto existing projects
157
+
158
+ ${utils.COLORS.bold}Usage:${utils.COLORS.reset}
159
+ bootspring onboard Start interactive onboarding
160
+ bootspring onboard status Show progress
161
+ bootspring onboard resume Continue from checkpoint
162
+ bootspring onboard detect Run detection only
163
+ bootspring onboard generate Generate config only
164
+ bootspring onboard reset Reset workflow
165
+
166
+ ${utils.COLORS.bold}Options:${utils.COLORS.reset}
167
+ --dry-run Preview without changes
168
+ --skip-optional Skip optional phases
169
+ --quick Force fast mode (auto for 500+ files)
170
+ --apply Apply generated files
171
+
172
+ ${utils.COLORS.bold}Notes:${utils.COLORS.reset}
173
+ Large codebases (500+ files) automatically use fast mode to skip
174
+ heavy dependency analysis. Use --quick to force it on smaller projects.
175
+
176
+ ${utils.COLORS.bold}Examples:${utils.COLORS.reset}
177
+ bootspring onboard Auto-detects and adjusts for codebase size
178
+ bootspring onboard status
179
+ bootspring onboard --dry-run
180
+ `);
181
+ }
182
+
183
+ /**
184
+ * Start onboarding workflow
185
+ */
186
+ async function onboardStart(args: ParsedArgs): Promise<void> {
187
+ const workflow = new OnboardWorkflowEngine(projectRoot);
188
+
189
+ // Check for existing workflow
190
+ if (workflow.hasWorkflow()) {
191
+ workflow.loadState();
192
+ const progress = workflow.getProgress();
193
+
194
+ if (!progress.isComplete) {
195
+ utils.print.info('Existing onboarding workflow found.');
196
+ console.log(` Progress: ${progress.overall.percentage}% complete`);
197
+ console.log(` Current phase: ${progress.currentPhase || 'none'}`);
198
+ console.log('');
199
+ utils.print.info('Use "bootspring onboard resume" to continue');
200
+ utils.print.info('Use "bootspring onboard reset" to start fresh');
201
+ return;
202
+ }
203
+ }
204
+
205
+ // Initialize new workflow
206
+ utils.print.header('Bootspring Onboarding');
207
+ console.log('Analyzing your existing codebase...\n');
208
+
209
+ workflow.initializeWorkflow();
210
+
211
+ // Run workflow
212
+ await runWorkflowLoop(workflow, args);
213
+ }
214
+
215
+ /**
216
+ * Resume onboarding workflow
217
+ */
218
+ async function onboardResume(args: ParsedArgs): Promise<void> {
219
+ const workflow = new OnboardWorkflowEngine(projectRoot);
220
+
221
+ if (!workflow.hasWorkflow()) {
222
+ utils.print.error('No existing workflow found.');
223
+ utils.print.info('Run "bootspring onboard" to start.');
224
+ return;
225
+ }
226
+
227
+ workflow.loadState();
228
+ const resumePoint = workflow.getResumePoint();
229
+
230
+ if (!resumePoint) {
231
+ utils.print.success('Onboarding workflow already complete!');
232
+ return;
233
+ }
234
+
235
+ utils.print.header('Resuming Onboarding');
236
+ console.log(`Continuing from: ${resumePoint.phaseName}`);
237
+ console.log('');
238
+
239
+ await runWorkflowLoop(workflow, args);
240
+ }
241
+
242
+ /**
243
+ * Run the workflow loop
244
+ */
245
+ async function runWorkflowLoop(workflow: OnboardWorkflowEngine, args: ParsedArgs): Promise<void> {
246
+ const dryRun = args['dry-run'] || false;
247
+ const skipOptional = args['skip-optional'] || false;
248
+ const quickMode = args.quick || false;
249
+
250
+ while (true) {
251
+ const nextPhaseId = workflow.getNextPhase();
252
+
253
+ if (!nextPhaseId) {
254
+ // Workflow complete
255
+ break;
256
+ }
257
+
258
+ const phase = ONBOARD_PHASES[nextPhaseId];
259
+ if (!phase) continue;
260
+
261
+ // Skip optional phases if requested
262
+ if (skipOptional && !phase.required) {
263
+ workflow.skipPhase(nextPhaseId);
264
+ utils.print.dim(`Skipping optional phase: ${phase.name}`);
265
+ continue;
266
+ }
267
+
268
+ // Run the phase
269
+ workflow.startPhase(nextPhaseId);
270
+ const spinner = utils.createSpinner(`Running: ${phase.name}`).start();
271
+
272
+ try {
273
+ let result: unknown;
274
+
275
+ switch (nextPhaseId) {
276
+ case 'detection': {
277
+ const detectionResult = await workflow.runDetection();
278
+ spinner.succeed(`Detection complete: Found ${detectionResult.stack.detected.length} technologies`);
279
+ displayDetectionResults(detectionResult);
280
+ result = detectionResult;
281
+ break;
282
+ }
283
+
284
+ case 'patterns': {
285
+ let patternResult: PatternResult;
286
+ if (quickMode) {
287
+ // Quick mode: skip heavy dependency analysis, just detect features
288
+ patternResult = await workflow.runQuickPatternScan();
289
+ spinner.succeed('Quick pattern scan complete');
290
+ } else {
291
+ patternResult = await workflow.runPatternScan();
292
+ if (patternResult.quickMode) {
293
+ // Auto-detected large codebase
294
+ spinner.succeed('Pattern scan complete (large codebase - quick mode)');
295
+ } else {
296
+ spinner.succeed(`Pattern scan complete: ${patternResult.architecture.patterns.length} patterns found`);
297
+ }
298
+ }
299
+ displayPatternResults(patternResult);
300
+ result = patternResult;
301
+ break;
302
+ }
303
+
304
+ case 'docs': {
305
+ const docResult = await workflow.runDocDiscovery();
306
+ spinner.succeed(`Doc discovery complete: ${docResult.found.length} docs found, ${docResult.missing.length} missing`);
307
+ displayDocResults(docResult);
308
+ result = docResult;
309
+ break;
310
+ }
311
+
312
+ case 'config': {
313
+ const configResult = await workflow.runConfigGeneration();
314
+ spinner.succeed('Configuration generated');
315
+ displayConfigResults(configResult, dryRun);
316
+ result = configResult;
317
+ break;
318
+ }
319
+
320
+ case 'baseline': {
321
+ const baselineResult = await workflow.runBaselineCapture();
322
+ spinner.succeed('Baseline captured');
323
+ result = baselineResult;
324
+ break;
325
+ }
326
+
327
+ case 'reverse':
328
+ // Skip for now - optional
329
+ spinner.info('Skipping reverse engineering (optional)');
330
+ workflow.skipPhase(nextPhaseId);
331
+ continue;
332
+
333
+ default:
334
+ spinner.warn(`Unknown phase: ${nextPhaseId}`);
335
+ workflow.skipPhase(nextPhaseId);
336
+ continue;
337
+ }
338
+
339
+ workflow.completePhase(nextPhaseId, result);
340
+
341
+ } catch (error) {
342
+ spinner.fail(`Failed: ${(error as Error).message}`);
343
+ workflow.failPhase(nextPhaseId, (error as Error).message);
344
+
345
+ if (phase.required) {
346
+ utils.print.error('Required phase failed. Cannot continue.');
347
+ return;
348
+ }
349
+ }
350
+
351
+ console.log('');
352
+ }
353
+
354
+ // Workflow complete
355
+ utils.print.success('Onboarding complete!');
356
+ console.log('');
357
+
358
+ const progress = workflow.getProgress();
359
+
360
+ // Apply files if requested
361
+ const shouldApply = args.apply || false;
362
+ if (shouldApply && !dryRun) {
363
+ utils.print.info('Applying generated files...');
364
+ workflow.applyGeneratedFiles();
365
+ utils.print.success('bootspring.config.js and CLAUDE.md created');
366
+ } else if (!dryRun) {
367
+ utils.print.info('Generated files are in .bootspring/onboard/generated/');
368
+ utils.print.info('Run with --apply to copy them to project root');
369
+ }
370
+
371
+ // Show summary
372
+ console.log('');
373
+ utils.print.header('Summary');
374
+ console.log(` Stack: ${formatStack(progress.detection?.stack)}`);
375
+ console.log(` Phases completed: ${progress.overall.completed}/${progress.overall.total}`);
376
+ console.log('');
377
+ utils.print.info('Next steps:');
378
+ console.log(' 1. Review generated configuration');
379
+ console.log(' 2. Run "bootspring analyze" for deeper insights');
380
+ console.log(' 3. Run "bootspring audit" for quality checks');
381
+ }
382
+
383
+ /**
384
+ * Display detection results
385
+ */
386
+ function displayDetectionResults(result: DetectionResult): void {
387
+ console.log('');
388
+
389
+ if (result.stack.framework) {
390
+ console.log(` ${utils.COLORS.cyan}Framework:${utils.COLORS.reset} ${result.stack.framework.name}`);
391
+ }
392
+ if (result.stack.language) {
393
+ console.log(` ${utils.COLORS.cyan}Language:${utils.COLORS.reset} ${result.stack.language.name}`);
394
+ }
395
+ if (result.stack.database) {
396
+ console.log(` ${utils.COLORS.cyan}Database:${utils.COLORS.reset} ${result.stack.database.name}`);
397
+ }
398
+ if (result.stack.hosting) {
399
+ console.log(` ${utils.COLORS.cyan}Hosting:${utils.COLORS.reset} ${result.stack.hosting.name}`);
400
+ }
401
+ if (result.stack.auth) {
402
+ console.log(` ${utils.COLORS.cyan}Auth:${utils.COLORS.reset} ${result.stack.auth.name}`);
403
+ }
404
+ if (result.stack.payments) {
405
+ console.log(` ${utils.COLORS.cyan}Payments:${utils.COLORS.reset} ${result.stack.payments.name}`);
406
+ }
407
+
408
+ console.log('');
409
+ console.log(` ${utils.COLORS.dim}Files: ${result.structure.stats.totalFiles} | Dirs: ${result.structure.stats.totalDirs}${utils.COLORS.reset}`);
410
+ }
411
+
412
+ /**
413
+ * Display pattern results
414
+ */
415
+ function displayPatternResults(result: PatternResult): void {
416
+ if (result.architecture.patterns.length > 0) {
417
+ console.log('');
418
+ console.log(' Detected patterns:');
419
+ for (const pattern of result.architecture.patterns.slice(0, 3)) {
420
+ console.log(` - ${pattern.name} (${pattern.confidence}%)`);
421
+ }
422
+ }
423
+
424
+ if (result.features) {
425
+ const activeFeatures = Object.entries(result.features)
426
+ .filter(([, v]) => v)
427
+ .map(([k]) => k);
428
+
429
+ if (activeFeatures.length > 0) {
430
+ console.log('');
431
+ console.log(' Detected features:');
432
+ console.log(` ${activeFeatures.join(', ')}`);
433
+ }
434
+ }
435
+ }
436
+
437
+ /**
438
+ * Display documentation results
439
+ */
440
+ function displayDocResults(result: DocResult): void {
441
+ if (result.found.length > 0) {
442
+ console.log('');
443
+ console.log(' Found documentation:');
444
+ for (const doc of result.found.slice(0, 5)) {
445
+ console.log(` ${utils.COLORS.green}✓${utils.COLORS.reset} ${doc.file}`);
446
+ }
447
+ }
448
+
449
+ if (result.missing.length > 0) {
450
+ console.log('');
451
+ console.log(' Missing (recommended):');
452
+ for (const doc of result.missing) {
453
+ console.log(` ${utils.COLORS.yellow}○${utils.COLORS.reset} ${doc.file}`);
454
+ }
455
+ }
456
+ }
457
+
458
+ /**
459
+ * Display config generation results
460
+ */
461
+ function displayConfigResults(result: ConfigResult, dryRun: boolean): void {
462
+ console.log('');
463
+
464
+ if (dryRun) {
465
+ console.log(` ${utils.COLORS.dim}[DRY RUN] Would generate:${utils.COLORS.reset}`);
466
+ } else {
467
+ console.log(' Generated:');
468
+ }
469
+
470
+ console.log(' - bootspring.config.js');
471
+ console.log(' - CLAUDE.md');
472
+ }
473
+
474
+ /**
475
+ * Format stack for display
476
+ */
477
+ function formatStack(stack: StackInfo | undefined): string {
478
+ if (!stack) return 'Unknown';
479
+
480
+ const parts: string[] = [];
481
+ if (stack.framework) parts.push(stack.framework.name);
482
+ if (stack.language) parts.push(stack.language.name);
483
+ if (stack.database) parts.push(stack.database.name);
484
+
485
+ return parts.join(' + ') || 'Unknown';
486
+ }
487
+
488
+ /**
489
+ * Show onboard status
490
+ */
491
+ async function onboardStatus(): Promise<void> {
492
+ const workflow = new OnboardWorkflowEngine(projectRoot);
493
+
494
+ if (!workflow.hasWorkflow()) {
495
+ utils.print.info('No onboarding workflow started.');
496
+ utils.print.info('Run "bootspring onboard" to begin.');
497
+ return;
498
+ }
499
+
500
+ workflow.loadState();
501
+ const progress = workflow.getProgress();
502
+
503
+ utils.print.header('Onboarding Status');
504
+
505
+ // Overall progress
506
+ console.log(`Progress: ${progress.overall.percentage}% complete`);
507
+ console.log(`Started: ${progress.startedAt ? new Date(progress.startedAt).toLocaleString() : 'N/A'}`);
508
+ console.log(`Last updated: ${progress.lastUpdated ? utils.formatRelativeTime(new Date(progress.lastUpdated)) : 'N/A'}`);
509
+ console.log('');
510
+
511
+ // Phase status
512
+ console.log('Phases:');
513
+ for (const phase of progress.phases) {
514
+ const statusIcon = getStatusIcon(phase.status);
515
+ const required = phase.required ? '' : ' (optional)';
516
+ console.log(` ${statusIcon} ${phase.name}${required}`);
517
+ }
518
+
519
+ console.log('');
520
+
521
+ // Detection summary if available
522
+ if (progress.detection?.stack) {
523
+ console.log('Detected stack:');
524
+ console.log(` ${formatStack(progress.detection.stack)}`);
525
+ }
526
+
527
+ if (!progress.isComplete) {
528
+ console.log('');
529
+ utils.print.info('Run "bootspring onboard resume" to continue');
530
+ }
531
+ }
532
+
533
+ /**
534
+ * Run detection only
535
+ */
536
+ async function onboardDetect(): Promise<void> {
537
+ const workflow = new OnboardWorkflowEngine(projectRoot);
538
+ workflow.initializeWorkflow();
539
+
540
+ const spinner = utils.createSpinner('Detecting stack...').start();
541
+
542
+ try {
543
+ const result = await workflow.runDetection();
544
+ spinner.succeed('Detection complete');
545
+
546
+ displayDetectionResults(result);
547
+ } catch (error) {
548
+ spinner.fail(`Detection failed: ${(error as Error).message}`);
549
+ }
550
+ }
551
+
552
+ /**
553
+ * Generate config only
554
+ */
555
+ async function onboardGenerate(args: ParsedArgs): Promise<void> {
556
+ const workflow = new OnboardWorkflowEngine(projectRoot);
557
+
558
+ // Need detection first
559
+ if (!workflow.hasWorkflow()) {
560
+ workflow.initializeWorkflow();
561
+
562
+ const spinner = utils.createSpinner('Running detection first...').start();
563
+ await workflow.runDetection();
564
+ workflow.completePhase('detection');
565
+
566
+ await workflow.runPatternScan();
567
+ workflow.completePhase('patterns');
568
+
569
+ spinner.succeed('Detection complete');
570
+ } else {
571
+ workflow.loadState();
572
+ }
573
+
574
+ const spinner = utils.createSpinner('Generating configuration...').start();
575
+
576
+ try {
577
+ const result = await workflow.runConfigGeneration();
578
+ spinner.succeed('Configuration generated');
579
+
580
+ displayConfigResults(result, args['dry-run'] || false);
581
+
582
+ if (args.apply) {
583
+ workflow.applyGeneratedFiles();
584
+ utils.print.success('Files applied to project root');
585
+ } else {
586
+ console.log('');
587
+ utils.print.info('Files saved to .bootspring/onboard/generated/');
588
+ utils.print.info('Run with --apply to copy to project root');
589
+ }
590
+ } catch (error) {
591
+ spinner.fail(`Generation failed: ${(error as Error).message}`);
592
+ }
593
+ }
594
+
595
+ /**
596
+ * Reset onboard workflow
597
+ */
598
+ async function onboardReset(): Promise<void> {
599
+ const workflow = new OnboardWorkflowEngine(projectRoot);
600
+
601
+ if (!workflow.hasWorkflow()) {
602
+ utils.print.info('No workflow to reset.');
603
+ return;
604
+ }
605
+
606
+ workflow.resetWorkflow();
607
+ utils.print.success('Onboarding workflow reset.');
608
+ }
609
+
610
+ /**
611
+ * Get status icon
612
+ */
613
+ function getStatusIcon(status: string): string {
614
+ switch (status) {
615
+ case 'completed':
616
+ return `${utils.COLORS.green}✓${utils.COLORS.reset}`;
617
+ case 'in_progress':
618
+ return `${utils.COLORS.cyan}●${utils.COLORS.reset}`;
619
+ case 'failed':
620
+ return `${utils.COLORS.red}✗${utils.COLORS.reset}`;
621
+ case 'skipped':
622
+ return `${utils.COLORS.dim}○${utils.COLORS.reset}`;
623
+ default:
624
+ return `${utils.COLORS.dim}○${utils.COLORS.reset}`;
625
+ }
626
+ }
627
+
628
+ /**
629
+ * Main entry point
630
+ */
631
+ export async function run(args: string[]): Promise<void> {
632
+ const parsedArgs = utils.parseArgs(args) as ParsedArgs;
633
+ const subcommand = parsedArgs._[0] || 'start';
634
+
635
+ switch (subcommand) {
636
+ case 'start':
637
+ return onboardStart(parsedArgs);
638
+
639
+ case 'status':
640
+ return onboardStatus();
641
+
642
+ case 'resume':
643
+ return onboardResume(parsedArgs);
644
+
645
+ case 'detect':
646
+ return onboardDetect();
647
+
648
+ case 'generate':
649
+ return onboardGenerate(parsedArgs);
650
+
651
+ case 'reset':
652
+ return onboardReset();
653
+
654
+ case 'help':
655
+ case '--help':
656
+ case '-h':
657
+ return showHelp();
658
+
659
+ default:
660
+ // If no subcommand, start workflow
661
+ return onboardStart(parsedArgs);
662
+ }
663
+ }