@friggframework/devtools 2.0.0--canary.549.70ef06a.0 → 2.0.0--canary.545.ccb5010.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 (127) hide show
  1. package/frigg-cli/README.md +1 -1
  2. package/frigg-cli/__tests__/application/use-cases/AddApiModuleToIntegrationUseCase.test.js +326 -0
  3. package/frigg-cli/__tests__/application/use-cases/CreateApiModuleUseCase.test.js +337 -0
  4. package/frigg-cli/__tests__/domain/entities/ApiModule.test.js +373 -0
  5. package/frigg-cli/__tests__/domain/entities/AppDefinition.test.js +313 -0
  6. package/frigg-cli/__tests__/domain/services/IntegrationValidator.test.js +269 -0
  7. package/frigg-cli/__tests__/domain/value-objects/IntegrationName.test.js +82 -0
  8. package/frigg-cli/__tests__/infrastructure/adapters/IntegrationJsUpdater.test.js +408 -0
  9. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemApiModuleRepository.test.js +583 -0
  10. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemAppDefinitionRepository.test.js +314 -0
  11. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemIntegrationRepository.test.js +383 -0
  12. package/frigg-cli/__tests__/unit/commands/build.test.js +1 -1
  13. package/frigg-cli/__tests__/unit/commands/doctor.test.js +0 -2
  14. package/frigg-cli/__tests__/unit/commands/init.test.js +406 -0
  15. package/frigg-cli/__tests__/unit/commands/install.test.js +23 -19
  16. package/frigg-cli/__tests__/unit/commands/provider-dispatch.test.js +383 -0
  17. package/frigg-cli/__tests__/unit/commands/repair.test.js +275 -0
  18. package/frigg-cli/__tests__/unit/dependencies.test.js +2 -2
  19. package/frigg-cli/__tests__/unit/start-command/application/RunPreflightChecksUseCase.test.js +411 -0
  20. package/frigg-cli/__tests__/unit/start-command/infrastructure/DatabaseAdapter.test.js +405 -0
  21. package/frigg-cli/__tests__/unit/start-command/infrastructure/DockerAdapter.test.js +496 -0
  22. package/frigg-cli/__tests__/unit/start-command/presentation/InteractivePromptAdapter.test.js +474 -0
  23. package/frigg-cli/__tests__/unit/utils/output.test.js +196 -0
  24. package/frigg-cli/application/use-cases/AddApiModuleToIntegrationUseCase.js +93 -0
  25. package/frigg-cli/application/use-cases/CreateApiModuleUseCase.js +93 -0
  26. package/frigg-cli/application/use-cases/CreateIntegrationUseCase.js +103 -0
  27. package/frigg-cli/build-command/index.js +123 -11
  28. package/frigg-cli/container.js +172 -0
  29. package/frigg-cli/deploy-command/index.js +83 -1
  30. package/frigg-cli/docs/OUTPUT_MIGRATION_GUIDE.md +286 -0
  31. package/frigg-cli/doctor-command/index.js +37 -16
  32. package/frigg-cli/domain/entities/ApiModule.js +272 -0
  33. package/frigg-cli/domain/entities/AppDefinition.js +227 -0
  34. package/frigg-cli/domain/entities/Integration.js +198 -0
  35. package/frigg-cli/domain/exceptions/DomainException.js +24 -0
  36. package/frigg-cli/domain/ports/IApiModuleRepository.js +53 -0
  37. package/frigg-cli/domain/ports/IAppDefinitionRepository.js +43 -0
  38. package/frigg-cli/domain/ports/IIntegrationRepository.js +61 -0
  39. package/frigg-cli/domain/services/IntegrationValidator.js +185 -0
  40. package/frigg-cli/domain/value-objects/IntegrationId.js +42 -0
  41. package/frigg-cli/domain/value-objects/IntegrationName.js +60 -0
  42. package/frigg-cli/domain/value-objects/SemanticVersion.js +70 -0
  43. package/frigg-cli/generate-iam-command.js +21 -1
  44. package/frigg-cli/index.js +21 -6
  45. package/frigg-cli/index.test.js +7 -2
  46. package/frigg-cli/infrastructure/UnitOfWork.js +46 -0
  47. package/frigg-cli/infrastructure/adapters/BackendJsUpdater.js +197 -0
  48. package/frigg-cli/infrastructure/adapters/FileSystemAdapter.js +224 -0
  49. package/frigg-cli/infrastructure/adapters/IntegrationJsUpdater.js +249 -0
  50. package/frigg-cli/infrastructure/adapters/SchemaValidator.js +92 -0
  51. package/frigg-cli/infrastructure/repositories/FileSystemApiModuleRepository.js +373 -0
  52. package/frigg-cli/infrastructure/repositories/FileSystemAppDefinitionRepository.js +116 -0
  53. package/frigg-cli/infrastructure/repositories/FileSystemIntegrationRepository.js +277 -0
  54. package/frigg-cli/init-command/backend-first-handler.js +124 -42
  55. package/frigg-cli/init-command/index.js +2 -1
  56. package/frigg-cli/init-command/template-handler.js +13 -3
  57. package/frigg-cli/install-command/backend-js.js +3 -3
  58. package/frigg-cli/install-command/environment-variables.js +16 -19
  59. package/frigg-cli/install-command/environment-variables.test.js +12 -13
  60. package/frigg-cli/install-command/index.js +14 -9
  61. package/frigg-cli/install-command/integration-file.js +3 -3
  62. package/frigg-cli/install-command/validate-package.js +5 -9
  63. package/frigg-cli/jest.config.js +4 -1
  64. package/frigg-cli/package-lock.json +16226 -0
  65. package/frigg-cli/repair-command/index.js +121 -128
  66. package/frigg-cli/start-command/application/RunPreflightChecksUseCase.js +376 -0
  67. package/frigg-cli/start-command/index.js +324 -2
  68. package/frigg-cli/start-command/infrastructure/DatabaseAdapter.js +591 -0
  69. package/frigg-cli/start-command/infrastructure/DockerAdapter.js +306 -0
  70. package/frigg-cli/start-command/presentation/InteractivePromptAdapter.js +329 -0
  71. package/frigg-cli/templates/backend/.env.example +62 -0
  72. package/frigg-cli/templates/backend/.eslintrc.json +12 -0
  73. package/frigg-cli/templates/backend/.prettierrc +6 -0
  74. package/frigg-cli/templates/backend/docker-compose.yml +22 -0
  75. package/frigg-cli/templates/backend/index.js +96 -0
  76. package/frigg-cli/templates/backend/infrastructure.js +12 -0
  77. package/frigg-cli/templates/backend/jest.config.js +17 -0
  78. package/frigg-cli/templates/backend/package.json +50 -0
  79. package/frigg-cli/templates/backend/src/api-modules/.gitkeep +10 -0
  80. package/frigg-cli/templates/backend/src/base/.gitkeep +7 -0
  81. package/frigg-cli/templates/backend/src/integrations/.gitkeep +10 -0
  82. package/frigg-cli/templates/backend/src/integrations/ExampleIntegration.js +65 -0
  83. package/frigg-cli/templates/backend/src/utils/.gitkeep +7 -0
  84. package/frigg-cli/templates/backend/test/setup.js +30 -0
  85. package/frigg-cli/templates/backend/ui-extensions/.gitkeep +0 -0
  86. package/frigg-cli/templates/backend/ui-extensions/README.md +77 -0
  87. package/frigg-cli/ui-command/index.js +58 -36
  88. package/frigg-cli/utils/__tests__/provider-helper.test.js +55 -0
  89. package/frigg-cli/utils/__tests__/repo-detection.test.js +436 -0
  90. package/frigg-cli/utils/output.js +382 -0
  91. package/frigg-cli/utils/provider-helper.js +75 -0
  92. package/frigg-cli/utils/repo-detection.js +85 -37
  93. package/frigg-cli/validate-command/__tests__/adapters/validate-command.test.js +205 -0
  94. package/frigg-cli/validate-command/__tests__/application/validate-app-use-case.test.js +104 -0
  95. package/frigg-cli/validate-command/__tests__/domain/fix-suggestion.test.js +153 -0
  96. package/frigg-cli/validate-command/__tests__/domain/validation-error.test.js +162 -0
  97. package/frigg-cli/validate-command/__tests__/domain/validation-result.test.js +152 -0
  98. package/frigg-cli/validate-command/__tests__/infrastructure/api-module-validator.test.js +332 -0
  99. package/frigg-cli/validate-command/__tests__/infrastructure/app-definition-validator.test.js +191 -0
  100. package/frigg-cli/validate-command/__tests__/infrastructure/integration-class-validator.test.js +146 -0
  101. package/frigg-cli/validate-command/__tests__/infrastructure/template-validation.test.js +155 -0
  102. package/frigg-cli/validate-command/adapters/cli/validate-command.js +199 -0
  103. package/frigg-cli/validate-command/application/use-cases/validate-app-use-case.js +35 -0
  104. package/frigg-cli/validate-command/domain/entities/validation-result.js +74 -0
  105. package/frigg-cli/validate-command/domain/value-objects/fix-suggestion.js +74 -0
  106. package/frigg-cli/validate-command/domain/value-objects/validation-error.js +68 -0
  107. package/frigg-cli/validate-command/infrastructure/validators/api-module-validator.js +181 -0
  108. package/frigg-cli/validate-command/infrastructure/validators/app-definition-validator.js +145 -0
  109. package/frigg-cli/validate-command/infrastructure/validators/integration-class-validator.js +113 -0
  110. package/infrastructure/create-frigg-infrastructure.js +93 -0
  111. package/infrastructure/docs/iam-policy-templates.md +1 -1
  112. package/infrastructure/domains/admin-scripts/admin-script-builder.js +200 -0
  113. package/infrastructure/domains/admin-scripts/admin-script-builder.test.js +499 -0
  114. package/infrastructure/domains/admin-scripts/index.js +5 -0
  115. package/infrastructure/domains/networking/vpc-builder.test.js +2 -4
  116. package/infrastructure/domains/networking/vpc-resolver.test.js +1 -1
  117. package/infrastructure/domains/shared/resource-discovery.js +5 -5
  118. package/infrastructure/domains/shared/types/app-definition.js +35 -0
  119. package/infrastructure/domains/shared/types/discovery-result.test.js +1 -1
  120. package/infrastructure/domains/shared/utilities/base-definition-factory.js +10 -1
  121. package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +2 -2
  122. package/infrastructure/infrastructure-composer.js +2 -0
  123. package/infrastructure/infrastructure-composer.test.js +2 -2
  124. package/infrastructure/jest.config.js +16 -0
  125. package/management-ui/README.md +245 -109
  126. package/package.json +8 -7
  127. package/frigg-cli/install-command/logger.js +0 -12
@@ -268,9 +268,17 @@ async function runPostDeploymentHealthCheck(stackName, options) {
268
268
  }
269
269
 
270
270
  async function deployCommand(options) {
271
+ const appDefinition = loadAppDefinition();
272
+
273
+ // Check if the app uses a non-AWS provider
274
+ const providerResult = loadProviderIfConfigured(appDefinition);
275
+ if (providerResult) {
276
+ return deployWithProvider(providerResult, options);
277
+ }
278
+
279
+ // Default: AWS deployment via serverless framework
271
280
  console.log('Deploying the serverless application...');
272
281
 
273
- const appDefinition = loadAppDefinition();
274
282
  const environment = validateAndBuildEnvironment(appDefinition, options);
275
283
 
276
284
  // Execute deployment
@@ -302,4 +310,78 @@ async function deployCommand(options) {
302
310
  }
303
311
  }
304
312
 
313
+ /**
314
+ * Check if the appDefinition specifies a non-AWS provider and resolve it.
315
+ * Returns null for AWS (default) so the caller falls through to existing behavior.
316
+ *
317
+ * @param {Object|null} appDefinition
318
+ * @returns {{ provider: Object, appDefinition: Object, providerName: string } | null}
319
+ */
320
+ function loadProviderIfConfigured(appDefinition) {
321
+ const providerName = appDefinition?.provider;
322
+ if (!providerName || providerName === 'aws') {
323
+ return null;
324
+ }
325
+
326
+ try {
327
+ const { resolveProvider } = require('@friggframework/core/providers/resolve-provider');
328
+ const provider = resolveProvider(appDefinition);
329
+ return { provider, appDefinition, providerName };
330
+ } catch (error) {
331
+ console.error(`Failed to load provider '${providerName}': ${error.message}`);
332
+ process.exit(1);
333
+ }
334
+ }
335
+
336
+ /**
337
+ * Deploy using a provider plugin (Netlify, etc.).
338
+ * Delegates entirely to the provider's deploy lifecycle:
339
+ * 1. validate() — check appDefinition for provider-specific issues
340
+ * 2. preflightCheck() — verify prerequisites (CLI tools, credentials)
341
+ * 3. deploy() — execute the deployment
342
+ */
343
+ async function deployWithProvider({ provider, appDefinition, providerName }, options) {
344
+ console.log(`Deploying with ${providerName} provider...`);
345
+
346
+ // 1. Validate appDefinition for this provider
347
+ if (typeof provider.validate === 'function') {
348
+ const validation = provider.validate(appDefinition);
349
+ if (validation.errors?.length > 0) {
350
+ console.error(`\nValidation errors for ${providerName}:`);
351
+ for (const error of validation.errors) {
352
+ console.error(` - ${error}`);
353
+ }
354
+ process.exit(1);
355
+ }
356
+ if (validation.warnings?.length > 0) {
357
+ for (const warning of validation.warnings) {
358
+ console.warn(` Warning: ${warning}`);
359
+ }
360
+ }
361
+ }
362
+
363
+ // 2. Deploy via provider
364
+ try {
365
+ const result = await provider.deploy(appDefinition, {
366
+ stage: options.stage,
367
+ prod: options.stage === 'production' || options.stage === 'prod',
368
+ dryRun: options.dryRun || false,
369
+ });
370
+
371
+ console.log(`\n✓ Deployment completed successfully!`);
372
+ if (result.url) {
373
+ console.log(` URL: ${result.url}`);
374
+ }
375
+ } catch (error) {
376
+ console.error(`\n✗ Deployment failed: ${error.message}`);
377
+ if (error.missing) {
378
+ console.error(' Missing prerequisites:');
379
+ for (const item of error.missing) {
380
+ console.error(` - ${item}`);
381
+ }
382
+ }
383
+ process.exit(1);
384
+ }
385
+ }
386
+
305
387
  module.exports = { deployCommand };
@@ -0,0 +1,286 @@
1
+ # Output Class Migration Guide
2
+
3
+ This guide shows how to migrate existing CLI commands to use the new unified `Output` class.
4
+
5
+ ## Why Migrate?
6
+
7
+ The unified `Output` class provides:
8
+ - **Consistency**: Same UI patterns across all commands
9
+ - **Better UX**: Spinners, progress bars, formatted tables
10
+ - **Maintainability**: Single place to update UI behavior
11
+ - **Testing**: Easier to mock and test
12
+
13
+ ## Before vs After
14
+
15
+ ### Before (Inconsistent)
16
+
17
+ ```javascript
18
+ // install-command/index.js (OLD - plain console.log)
19
+ function logError(message, error) {
20
+ console.error(message, error);
21
+ }
22
+
23
+ function logSuccess(message) {
24
+ console.log(message);
25
+ }
26
+
27
+ // Some other command (OLD - using chalk directly)
28
+ const chalk = require('chalk');
29
+ console.log(chalk.green('✓ Success'));
30
+ console.error(chalk.red('✗ Failed'));
31
+
32
+ // repair-command (OLD - using readline)
33
+ const readline = require('readline');
34
+ const rl = readline.createInterface({
35
+ input: process.stdin,
36
+ output: process.stdout
37
+ });
38
+ rl.question('Select option: ', (answer) => {
39
+ // ...
40
+ });
41
+ ```
42
+
43
+ ### After (Consistent)
44
+
45
+ ```javascript
46
+ // All commands use the same output utility
47
+ const output = require('./utils/output');
48
+
49
+ output.success('Operation completed');
50
+ output.error('Operation failed', error);
51
+
52
+ const spinner = output.spinner('Installing...');
53
+ // do work
54
+ spinner.succeed('Installed successfully');
55
+
56
+ const answer = await output.confirm('Continue with installation?');
57
+ ```
58
+
59
+ ## Migration Steps
60
+
61
+ ### Step 1: Import Output
62
+
63
+ Replace all imports of `chalk`, `console`, and `readline`:
64
+
65
+ ```diff
66
+ - const chalk = require('chalk');
67
+ - const readline = require('readline');
68
+ + const output = require('../utils/output');
69
+ ```
70
+
71
+ ### Step 2: Replace Console Methods
72
+
73
+ | Old | New |
74
+ |-----|-----|
75
+ | `console.log('Success')` | `output.success('Success')` |
76
+ | `console.error('Error')` | `output.error('Error')` |
77
+ | `console.info('Info')` | `output.info('Info')` |
78
+ | `console.warn('Warning')` | `output.warn('Warning')` |
79
+ | `console.log(chalk.green('✓ Done'))` | `output.success('Done')` |
80
+ | `console.error(chalk.red('✗ Failed'))` | `output.error('Failed')` |
81
+
82
+ ### Step 3: Replace Chalk Usage
83
+
84
+ ```diff
85
+ - console.log(chalk.blue('Starting...'));
86
+ + output.info('Starting...');
87
+
88
+ - console.log(chalk.bold('=== Header ==='));
89
+ + output.header('Header');
90
+
91
+ - console.log(chalk.yellow('⚠ Warning'));
92
+ + output.warn('Warning');
93
+ ```
94
+
95
+ ### Step 4: Replace Inquirer Prompts
96
+
97
+ ```diff
98
+ - const { confirm } = require('@inquirer/prompts');
99
+ - const answer = await confirm({ message: 'Continue?' });
100
+ + const answer = await output.confirm('Continue?');
101
+
102
+ - const { select } = require('@inquirer/prompts');
103
+ - const choice = await select({
104
+ - message: 'Select option',
105
+ - choices: ['Option 1', 'Option 2']
106
+ - });
107
+ + const choice = await output.select('Select option', ['Option 1', 'Option 2']);
108
+ ```
109
+
110
+ ### Step 5: Replace Readline (repair-command)
111
+
112
+ ```diff
113
+ - const readline = require('readline');
114
+ - const rl = readline.createInterface({
115
+ - input: process.stdin,
116
+ - output: process.stdout
117
+ - });
118
+ - rl.question('Select option: ', (answer) => {
119
+ - // handle answer
120
+ - rl.close();
121
+ - });
122
+ + const answer = await output.input('Select option:');
123
+ + // handle answer (no need to close)
124
+ ```
125
+
126
+ ### Step 6: Add Spinners for Long Operations
127
+
128
+ ```diff
129
+ - console.log('Installing dependencies...');
130
+ - await installDependencies();
131
+ - console.log('Done');
132
+ + const spinner = output.spinner('Installing dependencies...');
133
+ + await installDependencies();
134
+ + spinner.succeed('Dependencies installed');
135
+ ```
136
+
137
+ ### Step 7: Add Progress Bars
138
+
139
+ ```diff
140
+ - console.log(`Progress: ${i}/${total}`);
141
+ + output.progress(i, total, 'Processing files...');
142
+ ```
143
+
144
+ ### Step 8: Display Tables
145
+
146
+ ```diff
147
+ - modules.forEach(mod => {
148
+ - console.log(`${mod.name}\t${mod.version}\t${mod.status}`);
149
+ - });
150
+ + output.table(modules, ['name', 'version', 'status']);
151
+ ```
152
+
153
+ ## Real Example: install-command
154
+
155
+ ### Before
156
+
157
+ ```javascript
158
+ // install-command/logger.js
159
+ function logError(message, error) {
160
+ console.error(message, error);
161
+ if (error && error.stack) {
162
+ console.error(error.stack);
163
+ }
164
+ }
165
+
166
+ function logSuccess(message) {
167
+ console.log(message);
168
+ }
169
+
170
+ function logInfo(message) {
171
+ console.log(message);
172
+ }
173
+
174
+ module.exports = {
175
+ logError,
176
+ logSuccess,
177
+ logInfo
178
+ };
179
+
180
+ // install-command/index.js
181
+ const logger = require('./logger');
182
+
183
+ logger.logInfo('Starting installation...');
184
+ // ...
185
+ logger.logSuccess('Module installed successfully');
186
+ ```
187
+
188
+ ### After
189
+
190
+ ```javascript
191
+ // install-command/index.js
192
+ const output = require('../utils/output');
193
+
194
+ const spinner = output.spinner('Installing module...');
195
+ try {
196
+ // ... do installation
197
+ spinner.succeed('Module installed successfully');
198
+ } catch (error) {
199
+ spinner.fail('Installation failed');
200
+ output.error('Failed to install module', error);
201
+ process.exit(1);
202
+ }
203
+ ```
204
+
205
+ **Result**: Delete `install-command/logger.js` (no longer needed!)
206
+
207
+ ## Commands to Migrate
208
+
209
+ Priority order based on usage and inconsistency:
210
+
211
+ 1. **install-command** (HIGH) - Uses plain console.log, has trivial logger wrapper
212
+ 2. **repair-command** (HIGH) - Uses readline instead of inquirer
213
+ 3. **doctor-command** (MEDIUM) - Long-running, needs spinners
214
+ 4. **deploy-command** (MEDIUM) - Long-running, needs progress indication
215
+ 5. **start-command** (LOW) - Already uses chalk consistently
216
+ 6. **generate-command** (LOW) - Uses inquirer, but inconsistent colors
217
+ 7. **build-command** (LOW) - Simple command, less output
218
+
219
+ ## Testing Your Migration
220
+
221
+ After migrating a command:
222
+
223
+ 1. **Run the command** manually to verify output looks correct
224
+ 2. **Update tests** to mock `output` instead of `console`/`chalk`/`inquirer`
225
+ 3. **Check for** color consistency, spinner behavior, error messages
226
+
227
+ ### Test Example
228
+
229
+ ```javascript
230
+ // Before
231
+ jest.spyOn(console, 'log');
232
+ await myCommand();
233
+ expect(console.log).toHaveBeenCalledWith(expect.stringContaining('Success'));
234
+
235
+ // After
236
+ const output = require('../utils/output');
237
+ jest.spyOn(output, 'success');
238
+ await myCommand();
239
+ expect(output.success).toHaveBeenCalledWith('Operation completed');
240
+ ```
241
+
242
+ ## Output API Reference
243
+
244
+ ### Messages
245
+ - `output.success(message)` - Green checkmark + message
246
+ - `output.error(message, error?)` - Red X + message (+ stack trace if DEBUG=1)
247
+ - `output.info(message)` - Blue info icon + message
248
+ - `output.warn(message)` - Yellow warning icon + message
249
+ - `output.debug(message)` - Gray message (only if DEBUG=1)
250
+
251
+ ### Formatting
252
+ - `output.header(title)` - Bold cyan title with underline
253
+ - `output.separator()` - Gray horizontal line
254
+ - `output.newline()` - Blank line
255
+ - `output.table(data, columns?)` - Formatted table
256
+ - `output.keyValue(object)` - Key-value pairs
257
+ - `output.json(data, indent?)` - Syntax-highlighted JSON
258
+
259
+ ### Interactive
260
+ - `output.confirm(message, default?)` - Yes/no question
261
+ - `output.input(message, default?, validate?)` - Text input
262
+ - `output.select(message, choices, default?)` - Single selection
263
+ - `output.checkbox(message, choices)` - Multiple selection
264
+ - `output.password(message, validate?)` - Password input
265
+
266
+ ### Progress
267
+ - `output.spinner(text)` - Returns {update, succeed, fail, stop}
268
+ - `output.progress(current, total, message?)` - Progress bar
269
+
270
+ ### Compatibility
271
+ - `output.log(...args)` - Raw console.log (for gradual migration)
272
+
273
+ ## Benefits After Migration
274
+
275
+ - ✅ **32% less code** - Remove logger wrappers and boilerplate
276
+ - ✅ **100% consistency** - All commands look and feel the same
277
+ - ✅ **Better UX** - Spinners, progress bars, formatted tables
278
+ - ✅ **Easier testing** - Mock one module instead of many
279
+ - ✅ **Maintainable** - Update UI in one place
280
+
281
+ ## Questions?
282
+
283
+ See:
284
+ - `utils/output.js` - Full implementation
285
+ - `__tests__/unit/utils/output.test.js` - Test examples
286
+ - `FRIGG_CLI_ANALYSIS_REPORT.md` - Why we created this
@@ -12,8 +12,9 @@
12
12
  */
13
13
 
14
14
  const path = require('path');
15
+ const output = require('../utils/output');
15
16
  const fs = require('fs');
16
- const { select } = require('@inquirer/prompts');
17
+ // Using output.select from unified Output class (wraps @inquirer/prompts)
17
18
  const { CloudFormationClient, ListStacksCommand } = require('@aws-sdk/client-cloudformation');
18
19
 
19
20
  // Domain and Application Layer
@@ -160,9 +161,9 @@ function formatJsonOutput(report) {
160
161
  function writeOutputFile(content, filePath) {
161
162
  try {
162
163
  fs.writeFileSync(filePath, content, 'utf8');
163
- console.log(`\n✓ Report saved to: ${filePath}`);
164
+ output.success(` Report saved to: ${filePath}`);
164
165
  } catch (error) {
165
- console.error(`\n✗ Failed to write output file: ${error.message}`);
166
+ output.error(` Failed to write output file: ${error.message}`);
166
167
  process.exit(1);
167
168
  }
168
169
  }
@@ -204,17 +205,17 @@ async function listStacks(region) {
204
205
  * @returns {Promise<string>} Selected stack name
205
206
  */
206
207
  async function promptForStackSelection(region) {
207
- console.log(`\n🔍 Fetching CloudFormation stacks in ${region}...`);
208
+ output.info(`🔍 Fetching CloudFormation stacks in ${region}...`);
208
209
 
209
210
  const stacks = await listStacks(region);
210
211
 
211
212
  if (stacks.length === 0) {
212
- console.error(`\n✗ No CloudFormation stacks found in ${region}`);
213
- console.log(' Make sure you have stacks deployed and the correct AWS credentials configured.');
213
+ output.error(` No CloudFormation stacks found in ${region}`);
214
+ output.log(' Make sure you have stacks deployed and the correct AWS credentials configured.');
214
215
  process.exit(1);
215
216
  }
216
217
 
217
- console.log(`\n✓ Found ${stacks.length} stack(s)\n`);
218
+ output.success(` Found ${stacks.length} stack(s)\n`);
218
219
 
219
220
  // Create choices with stack name and metadata
220
221
  const choices = stacks.map(stack => {
@@ -230,7 +231,7 @@ async function promptForStackSelection(region) {
230
231
  };
231
232
  });
232
233
 
233
- const selectedStack = await select({
234
+ const selectedStack = await output.select({
234
235
  message: 'Select a stack to run health check:',
235
236
  choices,
236
237
  pageSize: 15,
@@ -246,6 +247,13 @@ async function promptForStackSelection(region) {
246
247
  */
247
248
  async function doctorCommand(stackName, options = {}) {
248
249
  try {
250
+ // Guard: doctor only works with AWS (CloudFormation stacks)
251
+ if (isNonAwsProvider()) {
252
+ output.error('The doctor command is only available for AWS deployments.');
253
+ output.log('Your appDefinition uses a non-AWS provider.');
254
+ process.exit(1);
255
+ }
256
+
249
257
  // Extract options with defaults
250
258
  const region = options.region || process.env.AWS_REGION || 'us-east-1';
251
259
  const format = options.format || 'console';
@@ -257,7 +265,7 @@ async function doctorCommand(stackName, options = {}) {
257
265
  }
258
266
 
259
267
  // Show progress to user (always, not just verbose mode)
260
- console.log(`\n🏥 Running health check on stack: ${stackName} (${region})\n`);
268
+ output.info(`🏥 Running health check on stack: ${stackName} (${region})\n`);
261
269
 
262
270
  // 1. Create stack identifier
263
271
  const stackIdentifier = new StackIdentifier({ stackName, region });
@@ -281,9 +289,9 @@ async function doctorCommand(stackName, options = {}) {
281
289
  // Progress callback to show execution status
282
290
  const progressCallback = (step, message) => {
283
291
  if (verbose) {
284
- console.log(` ${message}`);
292
+ output.log(` ${message}`);
285
293
  } else {
286
- console.log(`${step} ${message}`);
294
+ output.log(`${step} ${message}`);
287
295
  }
288
296
  };
289
297
 
@@ -292,7 +300,7 @@ async function doctorCommand(stackName, options = {}) {
292
300
  onProgress: progressCallback
293
301
  });
294
302
 
295
- console.log(' Health check complete!\n');
303
+ output.success(' Health check complete!\n');
296
304
 
297
305
  // 5. Format and output results
298
306
  if (format === 'json') {
@@ -301,11 +309,11 @@ async function doctorCommand(stackName, options = {}) {
301
309
  if (options.output) {
302
310
  writeOutputFile(jsonOutput, options.output);
303
311
  } else {
304
- console.log(jsonOutput);
312
+ output.log(jsonOutput);
305
313
  }
306
314
  } else {
307
315
  const consoleOutput = formatConsoleOutput(report, options);
308
- console.log(consoleOutput);
316
+ output.log(consoleOutput);
309
317
 
310
318
  if (options.output) {
311
319
  writeOutputFile(consoleOutput, options.output);
@@ -322,14 +330,27 @@ async function doctorCommand(stackName, options = {}) {
322
330
  process.exit(0);
323
331
  }
324
332
  } catch (error) {
325
- console.error(`\n✗ Health check failed: ${error.message}`);
333
+ output.error(` Health check failed: ${error.message}`);
326
334
 
327
335
  if (options.verbose && error.stack) {
328
- console.error(`\nStack trace:\n${error.stack}`);
336
+ output.error(`\nStack trace:\n${error.stack}`);
329
337
  }
330
338
 
331
339
  process.exit(1);
332
340
  }
333
341
  }
334
342
 
343
+ /**
344
+ * Check if the current appDefinition uses a non-AWS provider.
345
+ */
346
+ function isNonAwsProvider() {
347
+ try {
348
+ const { loadProviderForCli } = require('../utils/provider-helper');
349
+ const result = loadProviderForCli();
350
+ return result && result.providerName !== 'aws';
351
+ } catch {
352
+ return false;
353
+ }
354
+ }
355
+
335
356
  module.exports = { doctorCommand };