@friggframework/devtools 2.0.0--canary.522.923dfae.0 → 2.0.0--canary.540.c5ef83f.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 (119) hide show
  1. package/frigg-cli/README.md +1 -1
  2. package/frigg-cli/__tests__/unit/commands/doctor.test.js +2 -0
  3. package/frigg-cli/__tests__/unit/commands/install.test.js +19 -23
  4. package/frigg-cli/__tests__/unit/dependencies.test.js +2 -2
  5. package/frigg-cli/doctor-command/index.js +16 -17
  6. package/frigg-cli/index.js +6 -21
  7. package/frigg-cli/index.test.js +2 -7
  8. package/frigg-cli/init-command/backend-first-handler.js +42 -124
  9. package/frigg-cli/init-command/index.js +1 -2
  10. package/frigg-cli/init-command/template-handler.js +3 -13
  11. package/frigg-cli/install-command/backend-js.js +3 -3
  12. package/frigg-cli/install-command/environment-variables.js +19 -16
  13. package/frigg-cli/install-command/environment-variables.test.js +13 -12
  14. package/frigg-cli/install-command/index.js +9 -14
  15. package/frigg-cli/install-command/integration-file.js +3 -3
  16. package/frigg-cli/install-command/logger.js +12 -0
  17. package/frigg-cli/install-command/validate-package.js +9 -5
  18. package/frigg-cli/jest.config.js +1 -4
  19. package/frigg-cli/repair-command/index.js +128 -101
  20. package/frigg-cli/start-command/index.js +2 -246
  21. package/frigg-cli/ui-command/index.js +36 -58
  22. package/frigg-cli/utils/repo-detection.js +37 -85
  23. package/infrastructure/docs/iam-policy-templates.md +1 -1
  24. package/infrastructure/domains/networking/vpc-builder.test.js +4 -2
  25. package/infrastructure/domains/networking/vpc-resolver.test.js +1 -1
  26. package/infrastructure/domains/shared/cloudformation-discovery.test.js +7 -4
  27. package/infrastructure/domains/shared/resource-discovery.js +5 -5
  28. package/infrastructure/domains/shared/types/app-definition.js +0 -21
  29. package/infrastructure/domains/shared/types/discovery-result.test.js +1 -1
  30. package/infrastructure/domains/shared/utilities/base-definition-factory.js +1 -10
  31. package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +2 -2
  32. package/infrastructure/infrastructure-composer.test.js +2 -2
  33. package/management-ui/README.md +109 -245
  34. package/package.json +7 -8
  35. package/frigg-cli/__tests__/application/use-cases/AddApiModuleToIntegrationUseCase.test.js +0 -326
  36. package/frigg-cli/__tests__/application/use-cases/CreateApiModuleUseCase.test.js +0 -337
  37. package/frigg-cli/__tests__/domain/entities/ApiModule.test.js +0 -373
  38. package/frigg-cli/__tests__/domain/entities/AppDefinition.test.js +0 -313
  39. package/frigg-cli/__tests__/domain/services/IntegrationValidator.test.js +0 -269
  40. package/frigg-cli/__tests__/domain/value-objects/IntegrationName.test.js +0 -82
  41. package/frigg-cli/__tests__/infrastructure/adapters/IntegrationJsUpdater.test.js +0 -408
  42. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemApiModuleRepository.test.js +0 -583
  43. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemAppDefinitionRepository.test.js +0 -314
  44. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemIntegrationRepository.test.js +0 -383
  45. package/frigg-cli/__tests__/unit/commands/init.test.js +0 -406
  46. package/frigg-cli/__tests__/unit/commands/repair.test.js +0 -275
  47. package/frigg-cli/__tests__/unit/start-command/application/RunPreflightChecksUseCase.test.js +0 -411
  48. package/frigg-cli/__tests__/unit/start-command/infrastructure/DatabaseAdapter.test.js +0 -405
  49. package/frigg-cli/__tests__/unit/start-command/infrastructure/DockerAdapter.test.js +0 -496
  50. package/frigg-cli/__tests__/unit/start-command/presentation/InteractivePromptAdapter.test.js +0 -474
  51. package/frigg-cli/__tests__/unit/utils/output.test.js +0 -196
  52. package/frigg-cli/application/use-cases/AddApiModuleToIntegrationUseCase.js +0 -93
  53. package/frigg-cli/application/use-cases/CreateApiModuleUseCase.js +0 -93
  54. package/frigg-cli/application/use-cases/CreateIntegrationUseCase.js +0 -103
  55. package/frigg-cli/container.js +0 -172
  56. package/frigg-cli/docs/OUTPUT_MIGRATION_GUIDE.md +0 -286
  57. package/frigg-cli/domain/entities/ApiModule.js +0 -272
  58. package/frigg-cli/domain/entities/AppDefinition.js +0 -227
  59. package/frigg-cli/domain/entities/Integration.js +0 -198
  60. package/frigg-cli/domain/exceptions/DomainException.js +0 -24
  61. package/frigg-cli/domain/ports/IApiModuleRepository.js +0 -53
  62. package/frigg-cli/domain/ports/IAppDefinitionRepository.js +0 -43
  63. package/frigg-cli/domain/ports/IIntegrationRepository.js +0 -61
  64. package/frigg-cli/domain/services/IntegrationValidator.js +0 -185
  65. package/frigg-cli/domain/value-objects/IntegrationId.js +0 -42
  66. package/frigg-cli/domain/value-objects/IntegrationName.js +0 -60
  67. package/frigg-cli/domain/value-objects/SemanticVersion.js +0 -70
  68. package/frigg-cli/infrastructure/UnitOfWork.js +0 -46
  69. package/frigg-cli/infrastructure/adapters/BackendJsUpdater.js +0 -197
  70. package/frigg-cli/infrastructure/adapters/FileSystemAdapter.js +0 -224
  71. package/frigg-cli/infrastructure/adapters/IntegrationJsUpdater.js +0 -249
  72. package/frigg-cli/infrastructure/adapters/SchemaValidator.js +0 -92
  73. package/frigg-cli/infrastructure/repositories/FileSystemApiModuleRepository.js +0 -373
  74. package/frigg-cli/infrastructure/repositories/FileSystemAppDefinitionRepository.js +0 -116
  75. package/frigg-cli/infrastructure/repositories/FileSystemIntegrationRepository.js +0 -277
  76. package/frigg-cli/package-lock.json +0 -16226
  77. package/frigg-cli/start-command/application/RunPreflightChecksUseCase.js +0 -376
  78. package/frigg-cli/start-command/infrastructure/DatabaseAdapter.js +0 -591
  79. package/frigg-cli/start-command/infrastructure/DockerAdapter.js +0 -306
  80. package/frigg-cli/start-command/presentation/InteractivePromptAdapter.js +0 -329
  81. package/frigg-cli/templates/backend/.env.example +0 -62
  82. package/frigg-cli/templates/backend/.eslintrc.json +0 -12
  83. package/frigg-cli/templates/backend/.prettierrc +0 -6
  84. package/frigg-cli/templates/backend/docker-compose.yml +0 -22
  85. package/frigg-cli/templates/backend/index.js +0 -96
  86. package/frigg-cli/templates/backend/infrastructure.js +0 -12
  87. package/frigg-cli/templates/backend/jest.config.js +0 -17
  88. package/frigg-cli/templates/backend/package.json +0 -50
  89. package/frigg-cli/templates/backend/src/api-modules/.gitkeep +0 -10
  90. package/frigg-cli/templates/backend/src/base/.gitkeep +0 -7
  91. package/frigg-cli/templates/backend/src/integrations/.gitkeep +0 -10
  92. package/frigg-cli/templates/backend/src/integrations/ExampleIntegration.js +0 -65
  93. package/frigg-cli/templates/backend/src/utils/.gitkeep +0 -7
  94. package/frigg-cli/templates/backend/test/setup.js +0 -30
  95. package/frigg-cli/templates/backend/ui-extensions/.gitkeep +0 -0
  96. package/frigg-cli/templates/backend/ui-extensions/README.md +0 -77
  97. package/frigg-cli/utils/__tests__/repo-detection.test.js +0 -436
  98. package/frigg-cli/utils/output.js +0 -382
  99. package/frigg-cli/validate-command/__tests__/adapters/validate-command.test.js +0 -205
  100. package/frigg-cli/validate-command/__tests__/application/validate-app-use-case.test.js +0 -104
  101. package/frigg-cli/validate-command/__tests__/domain/fix-suggestion.test.js +0 -153
  102. package/frigg-cli/validate-command/__tests__/domain/validation-error.test.js +0 -162
  103. package/frigg-cli/validate-command/__tests__/domain/validation-result.test.js +0 -152
  104. package/frigg-cli/validate-command/__tests__/infrastructure/api-module-validator.test.js +0 -332
  105. package/frigg-cli/validate-command/__tests__/infrastructure/app-definition-validator.test.js +0 -191
  106. package/frigg-cli/validate-command/__tests__/infrastructure/integration-class-validator.test.js +0 -146
  107. package/frigg-cli/validate-command/__tests__/infrastructure/template-validation.test.js +0 -155
  108. package/frigg-cli/validate-command/adapters/cli/validate-command.js +0 -199
  109. package/frigg-cli/validate-command/application/use-cases/validate-app-use-case.js +0 -35
  110. package/frigg-cli/validate-command/domain/entities/validation-result.js +0 -74
  111. package/frigg-cli/validate-command/domain/value-objects/fix-suggestion.js +0 -74
  112. package/frigg-cli/validate-command/domain/value-objects/validation-error.js +0 -68
  113. package/frigg-cli/validate-command/infrastructure/validators/api-module-validator.js +0 -181
  114. package/frigg-cli/validate-command/infrastructure/validators/app-definition-validator.js +0 -128
  115. package/frigg-cli/validate-command/infrastructure/validators/integration-class-validator.js +0 -113
  116. package/infrastructure/domains/admin-scripts/admin-script-builder.js +0 -200
  117. package/infrastructure/domains/admin-scripts/admin-script-builder.test.js +0 -499
  118. package/infrastructure/domains/admin-scripts/index.js +0 -5
  119. package/infrastructure/jest.config.js +0 -16
@@ -12,7 +12,7 @@
12
12
  */
13
13
 
14
14
  const path = require('path');
15
- const output = require('../utils/output');
15
+ const readline = require('readline');
16
16
 
17
17
  // Domain and Application Layer
18
18
  const StackIdentifier = require('../../infrastructure/domains/health/domain/value-objects/stack-identifier');
@@ -34,6 +34,33 @@ const { TemplateParser } = require('../../infrastructure/domains/health/domain/s
34
34
  const { ImportTemplateGenerator } = require('../../infrastructure/domains/health/domain/services/import-template-generator');
35
35
  const { ImportProgressMonitor } = require('../../infrastructure/domains/health/domain/services/import-progress-monitor');
36
36
 
37
+ /**
38
+ * Create readline interface for user prompts
39
+ * @returns {readline.Interface}
40
+ */
41
+ function createReadlineInterface() {
42
+ return readline.createInterface({
43
+ input: process.stdin,
44
+ output: process.stdout,
45
+ });
46
+ }
47
+
48
+ /**
49
+ * Prompt user for confirmation
50
+ * @param {string} question - Question to ask
51
+ * @returns {Promise<boolean>} User confirmed
52
+ */
53
+ function confirm(question) {
54
+ const rl = createReadlineInterface();
55
+
56
+ return new Promise((resolve) => {
57
+ rl.question(`${question} (y/N): `, (answer) => {
58
+ rl.close();
59
+ resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
60
+ });
61
+ });
62
+ }
63
+
37
64
  /**
38
65
  * Handle import repair operation using template comparison
39
66
  * @param {StackIdentifier} stackIdentifier - Stack identifier
@@ -44,13 +71,13 @@ async function handleImportRepair(stackIdentifier, report, options) {
44
71
  const orphanedResources = report.getOrphanedResources();
45
72
 
46
73
  if (orphanedResources.length === 0) {
47
- output.success(' No orphaned resources to import');
74
+ console.log('\n✓ No orphaned resources to import');
48
75
  return { imported: 0, failed: 0 };
49
76
  }
50
77
 
51
- output.info(`📦 Found ${orphanedResources.length} orphaned resource(s) to import:`);
78
+ console.log(`\n📦 Found ${orphanedResources.length} orphaned resource(s) to import:`);
52
79
  orphanedResources.forEach((resource, idx) => {
53
- output.log(` ${idx + 1}. ${resource.resourceType} - ${resource.physicalId}`);
80
+ console.log(` ${idx + 1}. ${resource.resourceType} - ${resource.physicalId}`);
54
81
  });
55
82
 
56
83
  // Check for build template
@@ -58,12 +85,12 @@ async function handleImportRepair(stackIdentifier, report, options) {
58
85
  const buildTemplateExists = TemplateParser.buildTemplateExists();
59
86
 
60
87
  if (!buildTemplateExists) {
61
- output.warn(' Build template not found. Generating sequential logical IDs (not recommended).');
62
- output.log(` Run one of the following to generate build template:`);
63
- output.log(` • serverless package`);
64
- output.log(` • frigg build`);
65
- output.log(` • frigg deploy --stage dev`);
66
- output.log(` Then run 'frigg repair --import ${stackIdentifier.stackName}' again for correct logical IDs.\n`);
88
+ console.log('\n⚠️ Build template not found. Generating sequential logical IDs (not recommended).');
89
+ console.log(` Run one of the following to generate build template:`);
90
+ console.log(` • serverless package`);
91
+ console.log(` • frigg build`);
92
+ console.log(` • frigg deploy --stage dev`);
93
+ console.log(` Then run 'frigg repair --import ${stackIdentifier.stackName}' again for correct logical IDs.\n`);
67
94
 
68
95
  // Fallback to sequential IDs (old behavior)
69
96
  const resourcesToImport = orphanedResources.map((resource, idx) => ({
@@ -73,9 +100,9 @@ async function handleImportRepair(stackIdentifier, report, options) {
73
100
  }));
74
101
 
75
102
  if (!options.yes) {
76
- const confirmed = await output.confirm(`\nImport ${orphanedResources.length} orphaned resource(s) with sequential IDs?`);
103
+ const confirmed = await confirm(`\nImport ${orphanedResources.length} orphaned resource(s) with sequential IDs?`);
77
104
  if (!confirmed) {
78
- output.log('Import cancelled');
105
+ console.log('Import cancelled by user');
79
106
  return { imported: 0, failed: 0, cancelled: true };
80
107
  }
81
108
  }
@@ -84,20 +111,20 @@ async function handleImportRepair(stackIdentifier, report, options) {
84
111
  const resourceImporter = new AWSResourceImporter({ region: stackIdentifier.region });
85
112
  const repairUseCase = new RepairViaImportUseCase({ resourceDetector, resourceImporter });
86
113
 
87
- output.info('🔧 Importing resources with sequential IDs...');
114
+ console.log('\n🔧 Importing resources with sequential IDs...');
88
115
  const importResult = await repairUseCase.importMultipleResources({
89
116
  stackIdentifier,
90
117
  resources: resourcesToImport,
91
118
  });
92
119
 
93
120
  if (importResult.success) {
94
- output.success(` Successfully imported ${importResult.importedCount} resource(s)`);
121
+ console.log(`\n✓ Successfully imported ${importResult.importedCount} resource(s)`);
95
122
  } else {
96
- output.log(`\n✗ Import failed: ${importResult.message}`);
123
+ console.log(`\n✗ Import failed: ${importResult.message}`);
97
124
  if (importResult.validationErrors && importResult.validationErrors.length > 0) {
98
- output.log('\nValidation errors:');
125
+ console.log('\nValidation errors:');
99
126
  importResult.validationErrors.forEach((error) => {
100
- output.log(` • ${error.logicalId}: ${error.reason}`);
127
+ console.log(` • ${error.logicalId}: ${error.reason}`);
101
128
  });
102
129
  }
103
130
  }
@@ -110,9 +137,9 @@ async function handleImportRepair(stackIdentifier, report, options) {
110
137
  }
111
138
 
112
139
  // Use template comparison to find correct logical IDs
113
- output.info(`🔍 Analyzing templates to map orphaned resources to correct logical IDs...`);
114
- output.log(` Build template: ${buildTemplatePath}`);
115
- output.log(` Deployed template: CloudFormation (via AWS API)`);
140
+ console.log(`\n🔍 Analyzing templates to map orphaned resources to correct logical IDs...`);
141
+ console.log(` Build template: ${buildTemplatePath}`);
142
+ console.log(` Deployed template: CloudFormation (via AWS API)`);
116
143
 
117
144
  // Wire up use case with template comparison
118
145
  const stackRepository = new AWSStackRepository({ region: stackIdentifier.region });
@@ -132,31 +159,31 @@ async function handleImportRepair(stackIdentifier, report, options) {
132
159
  });
133
160
 
134
161
  if (!mappingResult.success) {
135
- output.log(`\n✗ Mapping failed: ${mappingResult.message}`);
162
+ console.log(`\n✗ Mapping failed: ${mappingResult.message}`);
136
163
  return { imported: 0, failed: 0, success: false };
137
164
  }
138
165
 
139
166
  // Display mapping results
140
- output.success(` Successfully mapped ${mappingResult.mappedCount} resource(s) to logical IDs:`);
167
+ console.log(`\n✅ Successfully mapped ${mappingResult.mappedCount} resource(s) to logical IDs:`);
141
168
  mappingResult.mappings.forEach((mapping) => {
142
- output.log(` • ${mapping.logicalId} ← ${mapping.physicalId} (${mapping.matchMethod}, ${mapping.confidence} confidence)`);
169
+ console.log(` • ${mapping.logicalId} ← ${mapping.physicalId} (${mapping.matchMethod}, ${mapping.confidence} confidence)`);
143
170
  });
144
171
 
145
172
  if (mappingResult.unmappedCount > 0) {
146
- output.warn(`️ Could not map ${mappingResult.unmappedCount} resource(s):`);
173
+ console.log(`\n⚠️ Could not map ${mappingResult.unmappedCount} resource(s):`);
147
174
  mappingResult.unmappedResources.forEach((resource) => {
148
- output.log(` • ${resource.resourceType} - ${resource.physicalId}`);
175
+ console.log(` • ${resource.resourceType} - ${resource.physicalId}`);
149
176
  });
150
177
  }
151
178
 
152
179
  // Display warnings for multiple resources of same type
153
180
  if (mappingResult.warnings && mappingResult.warnings.length > 0) {
154
- output.warn(`️ Warnings:`);
181
+ console.log(`\n⚠️ Warnings:`);
155
182
  mappingResult.warnings.forEach((warning) => {
156
- output.log(` • ${warning.message}`);
183
+ console.log(` • ${warning.message}`);
157
184
  if (warning.type === 'MULTIPLE_RESOURCES') {
158
185
  warning.resources.forEach((res) => {
159
- output.log(` - ${res.logicalId} ← ${res.physicalId} (${res.matchMethod}, ${res.confidence})`);
186
+ console.log(` - ${res.logicalId} ← ${res.physicalId} (${res.matchMethod}, ${res.confidence})`);
160
187
  });
161
188
  }
162
189
  });
@@ -164,20 +191,20 @@ async function handleImportRepair(stackIdentifier, report, options) {
164
191
 
165
192
  // Confirm with user (unless --yes flag)
166
193
  if (!options.yes) {
167
- output.info(`📋 The following will be imported into CloudFormation:`);
194
+ console.log(`\n📋 The following will be imported into CloudFormation:`);
168
195
  mappingResult.resourcesToImport.forEach((resource) => {
169
- output.log(` • ${resource.LogicalResourceId} (${resource.ResourceType})`);
196
+ console.log(` • ${resource.LogicalResourceId} (${resource.ResourceType})`);
170
197
  });
171
198
 
172
- const confirmed = await output.confirm(`\nProceed with import of ${mappingResult.mappedCount} resource(s)?`);
199
+ const confirmed = await confirm(`\nProceed with import of ${mappingResult.mappedCount} resource(s)?`);
173
200
  if (!confirmed) {
174
- output.log('Import cancelled');
201
+ console.log('Import cancelled by user');
175
202
  return { imported: 0, failed: 0, cancelled: true };
176
203
  }
177
204
  }
178
205
 
179
206
  // Execute actual CloudFormation import operation
180
- output.info(`🔧 Preparing CloudFormation import operation...`);
207
+ console.log(`\n🔧 Preparing CloudFormation import operation...`);
181
208
 
182
209
  // Wire up ExecuteResourceImportUseCase
183
210
  const templateParser = new TemplateParser();
@@ -210,38 +237,38 @@ async function handleImportRepair(stackIdentifier, report, options) {
210
237
  buildTemplatePath,
211
238
  onProgress: (progress) => {
212
239
  if (progress.step === 'generate_template' && progress.status === 'in_progress') {
213
- output.log(' • Generating import template...');
240
+ console.log(' • Generating import template...');
214
241
  } else if (progress.step === 'generate_template' && progress.status === 'complete') {
215
- output.log(' ✓ Template generated');
242
+ console.log(' ✓ Template generated');
216
243
  } else if (progress.step === 'create_change_set' && progress.status === 'in_progress') {
217
- output.log(' • Creating CloudFormation change set...');
244
+ console.log(' • Creating CloudFormation change set...');
218
245
  } else if (progress.step === 'create_change_set' && progress.status === 'complete') {
219
- output.log(` ✓ Change set created: ${progress.changeSetName}`);
246
+ console.log(` ✓ Change set created: ${progress.changeSetName}`);
220
247
  } else if (progress.step === 'wait_change_set' && progress.status === 'in_progress') {
221
- output.log(' • Waiting for change set...');
248
+ console.log(' • Waiting for change set...');
222
249
  } else if (progress.step === 'wait_change_set' && progress.status === 'complete') {
223
- output.log(' ✓ Change set ready');
250
+ console.log(' ✓ Change set ready');
224
251
  } else if (progress.step === 'execute_import' && progress.status === 'in_progress') {
225
252
  if (progress.resourceProgress) {
226
253
  const { logicalId, status, progress: resourceProgress, total } = progress.resourceProgress;
227
- output.log(` • Importing resource ${resourceProgress}/${total}: ${logicalId} (${status})`);
254
+ console.log(` • Importing resource ${resourceProgress}/${total}: ${logicalId} (${status})`);
228
255
  } else {
229
- output.log(' • Executing import operation...');
256
+ console.log(' • Executing import operation...');
230
257
  }
231
258
  } else if (progress.step === 'execute_import' && progress.status === 'complete') {
232
- output.log(' ✓ Import operation complete');
259
+ console.log(' ✓ Import operation complete');
233
260
  } else if (progress.step === 'verify' && progress.status === 'in_progress') {
234
- output.log(' • Verifying imported resources...');
261
+ console.log(' • Verifying imported resources...');
235
262
  } else if (progress.step === 'verify' && progress.status === 'complete') {
236
- output.log(' ✓ Verification complete');
263
+ console.log(' ✓ Verification complete');
237
264
  }
238
265
  },
239
266
  });
240
267
 
241
268
  if (importResult.success) {
242
- output.success(` Successfully imported ${importResult.importedCount} resource(s) into CloudFormation!`);
243
- output.log(` Stack status: ${importResult.stackStatus}`);
244
- output.log(` Change set: ${importResult.changeSetName}`);
269
+ console.log(`\n✅ Successfully imported ${importResult.importedCount} resource(s) into CloudFormation!`);
270
+ console.log(` Stack status: ${importResult.stackStatus}`);
271
+ console.log(` Change set: ${importResult.changeSetName}`);
245
272
 
246
273
  return {
247
274
  imported: importResult.importedCount,
@@ -249,8 +276,8 @@ async function handleImportRepair(stackIdentifier, report, options) {
249
276
  success: true,
250
277
  };
251
278
  } else {
252
- output.error(` Import operation failed: ${importResult.error}`);
253
- output.error(` Failed at step: ${importResult.step}`);
279
+ console.error(`\n❌ Import operation failed: ${importResult.error}`);
280
+ console.error(` Failed at step: ${importResult.step}`);
254
281
 
255
282
  return {
256
283
  imported: 0,
@@ -271,7 +298,7 @@ async function handleReconcileRepair(stackIdentifier, report, options) {
271
298
  const driftedResources = report.getDriftedResources();
272
299
 
273
300
  if (driftedResources.length === 0) {
274
- output.success(' No property drift to reconcile');
301
+ console.log('\n✓ No property drift to reconcile');
275
302
  return { reconciled: 0, failed: 0 };
276
303
  }
277
304
 
@@ -284,12 +311,12 @@ async function handleReconcileRepair(stackIdentifier, report, options) {
284
311
  totalMismatches += issues.length;
285
312
  });
286
313
 
287
- output.info(`🔧 Found ${driftedResources.length} drifted resource(s) with ${totalMismatches} property mismatch(es):`);
314
+ console.log(`\n🔧 Found ${driftedResources.length} drifted resource(s) with ${totalMismatches} property mismatch(es):`);
288
315
  driftedResources.forEach((resource) => {
289
316
  const issues = report.issues.filter(
290
317
  (issue) => issue.type === 'PROPERTY_MISMATCH' && issue.resourceId === resource.physicalId
291
318
  );
292
- output.log(` • ${resource.logicalId} (${resource.resourceType}): ${issues.length} mismatch(es)`);
319
+ console.log(` • ${resource.logicalId} (${resource.resourceType}): ${issues.length} mismatch(es)`);
293
320
  });
294
321
 
295
322
  // Determine mode (template or resource)
@@ -298,14 +325,14 @@ async function handleReconcileRepair(stackIdentifier, report, options) {
298
325
  ? 'Update CloudFormation template to match actual resource state'
299
326
  : 'Update cloud resources to match CloudFormation template';
300
327
 
301
- output.log(`\nReconciliation mode: ${mode}`);
302
- output.log(` ${modeDescription}`);
328
+ console.log(`\nReconciliation mode: ${mode}`);
329
+ console.log(` ${modeDescription}`);
303
330
 
304
331
  // Confirm with user (unless --yes flag)
305
332
  if (!options.yes) {
306
- const confirmed = await output.confirm(`\nReconcile ${totalMismatches} property mismatch(es) in ${mode} mode?`);
333
+ const confirmed = await confirm(`\nReconcile ${totalMismatches} property mismatch(es) in ${mode} mode?`);
307
334
  if (!confirmed) {
308
- output.log('Reconciliation cancelled');
335
+ console.log('Reconciliation cancelled by user');
309
336
  return { reconciled: 0, failed: 0, cancelled: true };
310
337
  }
311
338
  }
@@ -319,7 +346,7 @@ async function handleReconcileRepair(stackIdentifier, report, options) {
319
346
  const reconcileUseCase = new ReconcilePropertiesUseCase({ propertyReconciler });
320
347
 
321
348
  // Execute reconciliation for each drifted resource
322
- output.info('🔧 Reconciling property drift...');
349
+ console.log('\n🔧 Reconciling property drift...');
323
350
  let reconciledCount = 0;
324
351
  let failedCount = 0;
325
352
  let skippedImmutableCount = 0;
@@ -364,56 +391,56 @@ async function handleReconcileRepair(stackIdentifier, report, options) {
364
391
  });
365
392
  }
366
393
 
367
- output.log(` ✓ ${resource.logicalId}: Reconciled ${result.reconciledCount} property(ies)`);
394
+ console.log(` ✓ ${resource.logicalId}: Reconciled ${result.reconciledCount} property(ies)`);
368
395
  if (result.skippedCount > 0) {
369
- output.log(` ⚠ Skipped ${result.skippedCount} immutable property(ies) - requires manual intervention`);
396
+ console.log(` ⚠ Skipped ${result.skippedCount} immutable property(ies) - requires manual intervention`);
370
397
  }
371
398
 
372
399
  // Debug: Log full result if reconciledCount is 0 but we expected properties
373
400
  if (process.env.DEBUG_RECONCILE && result.reconciledCount === 0 && mismatches.length > 0) {
374
- output.log(` [DEBUG] Expected ${mismatches.length} mismatches, got result:`, JSON.stringify(result, null, 2));
401
+ console.log(` [DEBUG] Expected ${mismatches.length} mismatches, got result:`, JSON.stringify(result, null, 2));
375
402
  }
376
403
  } catch (error) {
377
404
  // Count failed properties, not just the resource
378
405
  failedCount += mismatches.length;
379
- output.log(` ✗ ${resource.logicalId}: ${error.message}`);
406
+ console.log(` ✗ ${resource.logicalId}: ${error.message}`);
380
407
 
381
408
  // Debug: Log full error
382
409
  if (process.env.DEBUG_RECONCILE) {
383
- output.log(` [DEBUG] Error stack:`, error.stack);
410
+ console.log(` [DEBUG] Error stack:`, error.stack);
384
411
  }
385
412
  }
386
413
  }
387
414
 
388
415
  // Report results
389
- output.log(''); // Blank line before summary
416
+ console.log(''); // Blank line before summary
390
417
 
391
418
  if (reconciledCount > 0) {
392
- output.log(`✅ Reconciled ${reconciledCount} property(ies)`);
419
+ console.log(`✅ Reconciled ${reconciledCount} property(ies)`);
393
420
  }
394
421
 
395
422
  if (skippedImmutableCount > 0) {
396
- output.warn(` ${skippedImmutableCount} immutable property(ies) require manual intervention:`);
423
+ console.log(`\n⚠ ${skippedImmutableCount} immutable property(ies) require manual intervention:`);
397
424
  immutableProperties.forEach(prop => {
398
- output.log(` • ${prop.logicalId}.${prop.propertyPath}`);
399
- output.log(` Template: ${JSON.stringify(prop.expectedValue)}`);
400
- output.log(` Actual: ${JSON.stringify(prop.actualValue)}`);
425
+ console.log(` • ${prop.logicalId}.${prop.propertyPath}`);
426
+ console.log(` Template: ${JSON.stringify(prop.expectedValue)}`);
427
+ console.log(` Actual: ${JSON.stringify(prop.actualValue)}`);
401
428
  });
402
429
 
403
- output.info(`💡 To resolve immutable property drift:`);
404
- output.log(` 1. These properties require resource replacement (cannot be updated in place)`);
405
- output.log(` 2. Options:`);
406
- output.log(` a) Accept the drift - update your local template to match actual values`);
407
- output.log(` b) Replace the resource - delete and recreate via CloudFormation`);
408
- output.log(` c) Use import workflow - remove from stack, then re-import with correct values`);
409
- output.log(`\n For automated import workflow (coming soon):`);
410
- output.log(` frigg repair --import-drift ${stackIdentifier.stackName}`);
430
+ console.log(`\n💡 To resolve immutable property drift:`);
431
+ console.log(` 1. These properties require resource replacement (cannot be updated in place)`);
432
+ console.log(` 2. Options:`);
433
+ console.log(` a) Accept the drift - update your local template to match actual values`);
434
+ console.log(` b) Replace the resource - delete and recreate via CloudFormation`);
435
+ console.log(` c) Use import workflow - remove from stack, then re-import with correct values`);
436
+ console.log(`\n For automated import workflow (coming soon):`);
437
+ console.log(` frigg repair --import-drift ${stackIdentifier.stackName}`);
411
438
  }
412
439
 
413
440
  if (failedCount === 0 && skippedImmutableCount === 0) {
414
- output.log(`✓ Successfully reconciled all ${reconciledCount} property mismatch(es)`);
441
+ console.log(`✓ Successfully reconciled all ${reconciledCount} property mismatch(es)`);
415
442
  } else {
416
- output.warn(` Reconciled ${reconciledCount} property(ies), ${failedCount} failed`);
443
+ console.log(`\n⚠ Reconciled ${reconciledCount} property(ies), ${failedCount} failed`);
417
444
  }
418
445
 
419
446
  return { reconciled: reconciledCount, failed: failedCount, success: failedCount === 0 };
@@ -428,19 +455,19 @@ async function repairCommand(stackName, options = {}) {
428
455
  try {
429
456
  // Validate required parameter
430
457
  if (!stackName) {
431
- output.error('Error: Stack name is required');
432
- output.log('Usage: frigg repair [options] <stack-name>');
433
- output.log('Options:');
434
- output.log(' --import Import orphaned resources');
435
- output.log(' --reconcile Reconcile property drift');
436
- output.log(' --yes Skip confirmation prompts');
458
+ console.error('Error: Stack name is required');
459
+ console.log('Usage: frigg repair [options] <stack-name>');
460
+ console.log('Options:');
461
+ console.log(' --import Import orphaned resources');
462
+ console.log(' --reconcile Reconcile property drift');
463
+ console.log(' --yes Skip confirmation prompts');
437
464
  process.exit(1);
438
465
  }
439
466
 
440
467
  // Validate at least one repair operation is selected
441
468
  if (!options.import && !options.reconcile) {
442
- output.error('Error: At least one repair operation must be specified (--import or --reconcile)');
443
- output.log('Usage: frigg repair [options] <stack-name>');
469
+ console.error('Error: At least one repair operation must be specified (--import or --reconcile)');
470
+ console.log('Usage: frigg repair [options] <stack-name>');
444
471
  process.exit(1);
445
472
  }
446
473
 
@@ -448,13 +475,13 @@ async function repairCommand(stackName, options = {}) {
448
475
  const region = options.region || process.env.AWS_REGION || 'us-east-1';
449
476
  const verbose = options.verbose || false;
450
477
 
451
- output.info(`🏥 Running Frigg Repair on stack: ${stackName} (${region})`);
478
+ console.log(`\n🏥 Running Frigg Repair on stack: ${stackName} (${region})`);
452
479
 
453
480
  // 1. Create stack identifier
454
481
  const stackIdentifier = new StackIdentifier({ stackName, region });
455
482
 
456
483
  // 2. Run health check first to identify issues
457
- output.info('🔍 Running health check to identify issues...');
484
+ console.log('\n🔍 Running health check to identify issues...');
458
485
 
459
486
  const stackRepository = new AWSStackRepository({ region });
460
487
  const resourceDetector = new AWSResourceDetector({ region });
@@ -470,8 +497,8 @@ async function repairCommand(stackName, options = {}) {
470
497
 
471
498
  const report = await runHealthCheckUseCase.execute({ stackIdentifier });
472
499
 
473
- output.log(`\nHealth Score: ${report.healthScore.value}/100 (${report.healthScore.qualitativeAssessment()})`);
474
- output.log(`Issues: ${report.getIssueCount()} total (${report.getCriticalIssueCount()} critical)`);
500
+ console.log(`\nHealth Score: ${report.healthScore.value}/100 (${report.healthScore.qualitativeAssessment()})`);
501
+ console.log(`Issues: ${report.getIssueCount()} total (${report.getCriticalIssueCount()} critical)`);
475
502
 
476
503
  // 3. Execute requested repair operations
477
504
  const results = {
@@ -497,24 +524,24 @@ async function repairCommand(stackName, options = {}) {
497
524
  }
498
525
 
499
526
  // 4. Final summary
500
- output.log('\n' + '═'.repeat(80));
501
- output.log('Repair Summary:');
527
+ console.log('\n' + '═'.repeat(80));
528
+ console.log('Repair Summary:');
502
529
  if (options.import) {
503
- output.log(` Imported: ${results.imported} resource(s)`);
530
+ console.log(` Imported: ${results.imported} resource(s)`);
504
531
  }
505
532
  if (options.reconcile) {
506
- output.log(` Reconciled: ${results.reconciled} property(ies)`);
533
+ console.log(` Reconciled: ${results.reconciled} property(ies)`);
507
534
  }
508
- output.log(` Failed: ${results.failed}`);
509
- output.log('═'.repeat(80));
535
+ console.log(` Failed: ${results.failed}`);
536
+ console.log('═'.repeat(80));
510
537
 
511
538
  // Run health check again to verify repairs
512
- output.info('🔍 Running health check to verify repairs...');
539
+ console.log('\n🔍 Running health check to verify repairs...');
513
540
  const verifyReport = await runHealthCheckUseCase.execute({ stackIdentifier });
514
- output.log(`\nNew Health Score: ${verifyReport.healthScore.value}/100 (${verifyReport.healthScore.qualitativeAssessment()})`);
541
+ console.log(`\nNew Health Score: ${verifyReport.healthScore.value}/100 (${verifyReport.healthScore.qualitativeAssessment()})`);
515
542
 
516
543
  if (verifyReport.healthScore.value > report.healthScore.value) {
517
- output.success(` Health improved by ${verifyReport.healthScore.value - report.healthScore.value} points!`);
544
+ console.log(`\n✓ Health improved by ${verifyReport.healthScore.value - report.healthScore.value} points!`);
518
545
  }
519
546
 
520
547
  // 5. Exit with appropriate code
@@ -524,10 +551,10 @@ async function repairCommand(stackName, options = {}) {
524
551
  process.exit(0);
525
552
  }
526
553
  } catch (error) {
527
- output.error(` Repair failed: ${error.message}`);
554
+ console.error(`\n✗ Repair failed: ${error.message}`);
528
555
 
529
556
  if (options.verbose && error.stack) {
530
- output.error(`\nStack trace:\n${error.stack}`);
557
+ console.error(`\nStack trace:\n${error.stack}`);
531
558
  }
532
559
 
533
560
  process.exit(1);