@paulduvall/claude-dev-toolkit 0.0.1-alpha.7 → 0.0.1-alpha.9

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 (35) hide show
  1. package/README.md +29 -9
  2. package/bin/claude-commands +184 -6
  3. package/hooks/lib/argument-parser.sh +0 -0
  4. package/hooks/lib/config-constants.sh +0 -0
  5. package/hooks/lib/context-manager.sh +0 -0
  6. package/hooks/lib/error-handler.sh +0 -0
  7. package/hooks/lib/execution-engine.sh +0 -0
  8. package/hooks/lib/file-utils.sh +0 -0
  9. package/hooks/lib/subagent-discovery.sh +0 -0
  10. package/hooks/lib/subagent-validator.sh +0 -0
  11. package/lib/backup-restore-command.js +140 -0
  12. package/lib/base/base-command.js +252 -0
  13. package/lib/base/command-result.js +184 -0
  14. package/lib/config/constants.js +255 -0
  15. package/lib/config.js +48 -6
  16. package/lib/configure-command.js +428 -0
  17. package/lib/dependency-validator.js +64 -5
  18. package/lib/installation-instruction-generator-backup.js +579 -0
  19. package/lib/installation-instruction-generator.js +213 -495
  20. package/lib/installer.js +134 -52
  21. package/lib/oidc-command.js +270 -0
  22. package/lib/services/backup-list-service.js +226 -0
  23. package/lib/services/backup-service.js +230 -0
  24. package/lib/services/command-installer-service.js +217 -0
  25. package/lib/services/logger-service.js +201 -0
  26. package/lib/services/package-manager-service.js +319 -0
  27. package/lib/services/platform-instruction-service.js +294 -0
  28. package/lib/services/recovery-instruction-service.js +348 -0
  29. package/lib/services/restore-service.js +221 -0
  30. package/lib/setup-command.js +309 -0
  31. package/lib/utils/claude-path-config.js +184 -0
  32. package/lib/utils/file-system-utils.js +152 -0
  33. package/lib/utils.js +8 -4
  34. package/lib/verify-command.js +430 -0
  35. package/package.json +4 -2
package/README.md CHANGED
@@ -49,6 +49,7 @@ claude-commands install --all # Install all 58 commands
49
49
  ```bash
50
50
  claude-commands list # See all available commands
51
51
  claude-commands status # Check installation status
52
+ claude-commands oidc --help # Configure GitHub Actions OIDC with AWS
52
53
  ```
53
54
 
54
55
  ## šŸ“‹ Available Commands
@@ -97,6 +98,11 @@ claude-commands config --list # List available templates
97
98
  claude-commands config --template <name> # Apply configuration template
98
99
  claude-commands config --help # Show config command help
99
100
 
101
+ # OIDC Configuration (NEW)
102
+ claude-commands oidc --help # Show OIDC configuration options
103
+ claude-commands oidc --dry-run # Preview OIDC setup actions
104
+ claude-commands oidc --region us-west-2 --stack-name my-oidc # Configure AWS OIDC
105
+
100
106
  # Subagents Management
101
107
  claude-commands subagents --list # List available subagents
102
108
  claude-commands subagents --install # Install subagents to Claude Code
@@ -174,8 +180,8 @@ npm run lint # Code linting
174
180
  - **Configuration Tests**: Template and setup validation
175
181
 
176
182
  ### Architecture
177
- - **Symlink Consolidation**: Single source of truth with root directory
178
- - **JavaScript-Based**: Migrated from Python for better Node.js integration
183
+ - **Self-Contained Package**: No dependencies on repository cloning
184
+ - **JavaScript-Based**: Native Node.js integration with comprehensive testing
179
185
  - **Modular Design**: Separate installer, config, and validation modules
180
186
  - **Cross-Platform**: Works on macOS, Linux, and Windows
181
187
 
@@ -212,13 +218,13 @@ npm run validate # Validate package
212
218
 
213
219
  ### Validation Commands
214
220
  ```bash
215
- # Repository validation (from main repo)
216
- ./validate-commands.sh # JavaScript-based validation
217
- ./verify-setup.sh # Complete setup verification
218
-
219
221
  # Package validation
220
222
  npm run validate # Package structure validation
221
223
  npm test # Comprehensive test suite
224
+
225
+ # CLI validation
226
+ claude-commands verify # Complete setup verification
227
+ claude-commands list # Check installed commands
222
228
  ```
223
229
 
224
230
  ## šŸ“š Documentation
@@ -252,8 +258,8 @@ npm test
252
258
  ```
253
259
 
254
260
  ### Adding Commands
255
- 1. Create command files in root `slash-commands/active/` or `slash-commands/experiments/`
256
- 2. Commands automatically sync to NPM package via symlinks
261
+ 1. Create command files in repository `slash-commands/active/` or `slash-commands/experiments/`
262
+ 2. Commands are included in NPM package through build process
257
263
  3. Validate with `npm run test:commands`
258
264
  4. Follow existing patterns and security guidelines
259
265
 
@@ -264,7 +270,21 @@ npm test
264
270
 
265
271
  ## šŸ”„ Recent Updates
266
272
 
267
- ### Version 0.0.1-alpha.2
273
+ ### Version 0.0.1-alpha.9
274
+ - āœ… **NEW: OIDC Command**: GitHub Actions to AWS OIDC integration (`claude-commands oidc`)
275
+ - āœ… **TDD Implementation**: Phase 1 foundation with comprehensive test coverage (14/14 tests)
276
+ - āœ… **CLI Integration**: Full command-line interface with help, dry-run, and configuration options
277
+ - āœ… **Error Framework**: Enhanced error handling with context-aware recovery suggestions
278
+ - āœ… **Dependency Validation**: AWS CLI, Git, and GitHub CLI availability checking
279
+
280
+ ### Version 0.0.1-alpha.8
281
+ - āœ… **Documentation Consistency**: Fixed command counts (58) and subagent counts (26)
282
+ - āœ… **Test Infrastructure**: Enhanced test-results directory handling
283
+ - āœ… **Repository Cleanup**: Removed outdated documentation and test artifacts
284
+ - āœ… **Validation Logic**: Improved scenario-aware test validation
285
+ - āœ… **Version Alignment**: Synchronized all version references
286
+
287
+ ### Version 0.0.1-alpha.7
268
288
  - āœ… **NPM Scoped Package**: Published as `@paulduvall/claude-dev-toolkit`
269
289
  - āœ… **Configuration Command**: Built-in `config` command for template management
270
290
  - āœ… **Workflow Reporting**: Comprehensive GitHub Actions reporting
@@ -48,13 +48,25 @@ program
48
48
 
49
49
  program
50
50
  .command('install')
51
- .description('Install command sets')
52
- .option('--active', 'Install active commands only')
53
- .option('--experiments', 'Install experimental commands')
54
- .option('--all', 'Install all commands')
55
- .action((options) => {
51
+ .description('Install command sets to ~/.claude/commands/')
52
+ .option('--active', 'Install production-ready commands (default)')
53
+ .option('--experiments', 'Install experimental commands only')
54
+ .option('--all', 'Install both active and experimental')
55
+ .option('--include <pattern>', 'Include specific commands matching pattern')
56
+ .option('--exclude <pattern>', 'Exclude commands matching pattern')
57
+ .option('--dry-run', 'Show what would be installed without making changes')
58
+ .option('--backup', 'Create backup before installation')
59
+ .action(async (options) => {
56
60
  const installer = require('../lib/installer');
57
- installer.install(options);
61
+ try {
62
+ const result = await installer.install(options);
63
+ if (!result.success && !result.dryRun) {
64
+ process.exit(1);
65
+ }
66
+ } catch (error) {
67
+ console.error(`Installation failed: ${error.message}`);
68
+ process.exit(1);
69
+ }
58
70
  });
59
71
 
60
72
  program
@@ -115,4 +127,170 @@ program
115
127
  config.handleCommand(options);
116
128
  });
117
129
 
130
+ program
131
+ .command('configure')
132
+ .description('Configure Claude Code settings (replaces configure-claude-code.sh)')
133
+ .option('--template <name>', 'Apply named template')
134
+ .option('--interactive', 'Launch interactive configuration wizard')
135
+ .option('--validate', 'Validate current configuration')
136
+ .option('--reset', 'Reset to default configuration')
137
+ .option('--backup', 'Create backup before changes (default: true)')
138
+ .option('--no-backup', 'Skip backup creation')
139
+ .action(async (options) => {
140
+ const ConfigureCommand = require('../lib/configure-command');
141
+ const configureCmd = new ConfigureCommand();
142
+ try {
143
+ const result = await configureCmd.execute(options);
144
+ if (!result.success) {
145
+ process.exit(1);
146
+ }
147
+ } catch (error) {
148
+ console.error(`Configuration failed: ${error.message}`);
149
+ process.exit(1);
150
+ }
151
+ });
152
+
153
+ program
154
+ .command('setup')
155
+ .description('Setup the Claude Dev Toolkit with custom commands and configuration')
156
+ .option('--type <template>', 'Configuration template to apply (basic, comprehensive, security-focused)')
157
+ .option('--commands <set>', 'Command set to install (active, experiments, all, none)')
158
+ .option('--skip-configure', 'Skip configuration step')
159
+ .option('--skip-hooks', 'Skip hooks installation')
160
+ .option('--force', 'Overwrite existing installation')
161
+ .option('--dry-run', 'Preview actions without executing')
162
+ .action(async (options) => {
163
+ const SetupCommand = require('../lib/setup-command');
164
+ const setupCmd = new SetupCommand();
165
+ try {
166
+ const result = await setupCmd.execute(options);
167
+ if (!result.success && !result.dryRun) {
168
+ process.exit(1);
169
+ }
170
+ } catch (error) {
171
+ console.error(`Setup failed: ${error.message}`);
172
+ process.exit(1);
173
+ }
174
+ });
175
+
176
+ program
177
+ .command('verify')
178
+ .description('Verify the Claude Dev Toolkit installation status and health')
179
+ .option('--verbose', 'Show detailed verification information')
180
+ .option('--fix', 'Attempt to fix detected issues automatically')
181
+ .action(async (options) => {
182
+ const VerifyCommand = require('../lib/verify-command');
183
+ const verifyCmd = new VerifyCommand();
184
+ try {
185
+ const result = await verifyCmd.execute(options);
186
+ // Set exit code based on health status
187
+ if (result.overall === 'critical') {
188
+ process.exit(2);
189
+ } else if (result.overall === 'warning') {
190
+ process.exit(1);
191
+ }
192
+ // Exit 0 for healthy
193
+ } catch (error) {
194
+ console.error(`Verification failed: ${error.message}`);
195
+ process.exit(2);
196
+ }
197
+ });
198
+
199
+ program
200
+ .command('backup [name]')
201
+ .description('Create named backup of Claude Code configuration')
202
+ .action(async (name) => {
203
+ const BackupRestoreCommand = require('../lib/backup-restore-command');
204
+ const backupCmd = new BackupRestoreCommand();
205
+ try {
206
+ const result = await backupCmd.backup(name);
207
+ if (!result.success) {
208
+ process.exit(1);
209
+ }
210
+ } catch (error) {
211
+ console.error(`Backup failed: ${error.message}`);
212
+ process.exit(1);
213
+ }
214
+ });
215
+
216
+ program
217
+ .command('restore <name>')
218
+ .description('Restore from a named backup')
219
+ .action(async (name) => {
220
+ const BackupRestoreCommand = require('../lib/backup-restore-command');
221
+ const restoreCmd = new BackupRestoreCommand();
222
+ try {
223
+ const result = await restoreCmd.restore(name);
224
+ if (!result.success) {
225
+ process.exit(1);
226
+ }
227
+ } catch (error) {
228
+ console.error(`Restore failed: ${error.message}`);
229
+ process.exit(1);
230
+ }
231
+ });
232
+
233
+ program
234
+ .command('oidc')
235
+ .description('Configure GitHub Actions OIDC integration with AWS')
236
+ .option('--region <region>', 'AWS region for OIDC setup', 'us-east-1')
237
+ .option('--stack-name <name>', 'CloudFormation stack name', 'github-oidc-stack')
238
+ .option('--role-name <name>', 'IAM role name for GitHub Actions', 'GitHubActionsRole')
239
+ .option('--repository-path <path>', 'Path to repository for OIDC setup', process.cwd())
240
+ .option('--dry-run', 'Preview actions without making changes')
241
+ .option('--verbose', 'Show detailed output')
242
+ .action(async (options) => {
243
+ const OidcCommand = require('../lib/oidc-command');
244
+ const oidcCmd = new OidcCommand();
245
+ try {
246
+ const result = await oidcCmd.execute(options);
247
+ if (!result.success && !result.dryRun) {
248
+ process.exit(1);
249
+ }
250
+ } catch (error) {
251
+ console.error(`OIDC setup failed: ${error.message}`);
252
+ process.exit(1);
253
+ }
254
+ });
255
+
256
+ program
257
+ .command('update')
258
+ .description('Check for package updates')
259
+ .action(async () => {
260
+ const { version } = require('../package.json');
261
+ console.log('šŸ” Checking for updates...\n');
262
+ console.log(`Current version: ${version}`);
263
+
264
+ try {
265
+ const { execSync } = require('child_process');
266
+ const output = execSync('npm view @paulduvall/claude-dev-toolkit version', {
267
+ encoding: 'utf8',
268
+ stdio: 'pipe'
269
+ }).trim();
270
+
271
+ console.log(`Latest version: ${output}`);
272
+
273
+ if (output !== version) {
274
+ console.log('\nšŸ†• Update available!');
275
+ console.log('\nTo update, run:');
276
+ console.log(' npm update -g @paulduvall/claude-dev-toolkit');
277
+
278
+ // Check for breaking changes in major version
279
+ const currentMajor = parseInt(version.split('.')[0]);
280
+ const latestMajor = parseInt(output.split('.')[0]);
281
+
282
+ if (latestMajor > currentMajor) {
283
+ console.log('\nāš ļø Major version update - may contain breaking changes');
284
+ console.log(' Review release notes before updating');
285
+ }
286
+ } else {
287
+ console.log('\nāœ… You are using the latest version');
288
+ }
289
+ } catch (error) {
290
+ console.error('āŒ Could not check for updates');
291
+ console.log(' Please check your internet connection');
292
+ process.exit(1);
293
+ }
294
+ });
295
+
118
296
  program.parse(process.argv);
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Backup and Restore Commands Implementation - Refactored
3
+ * Orchestrator for backup and restore operations using focused services
4
+ */
5
+
6
+ const BackupService = require('./services/backup-service');
7
+ const RestoreService = require('./services/restore-service');
8
+ const BackupListService = require('./services/backup-list-service');
9
+ const BaseCommand = require('./base/base-command');
10
+ const FileSystemUtils = require('./utils/file-system-utils');
11
+ const { execSync } = require('child_process');
12
+
13
+ class BackupRestoreCommand extends BaseCommand {
14
+ constructor(config = null) {
15
+ super(config);
16
+ this.backupService = new BackupService(this.config);
17
+ this.restoreService = new RestoreService(this.config);
18
+ this.listService = new BackupListService(this.config);
19
+ }
20
+
21
+ /**
22
+ * Create a backup of the entire .claude directory
23
+ */
24
+ async backup(name = null) {
25
+ this.logger.step('Creating backup of Claude Code configuration', { backupName: name });
26
+
27
+ try {
28
+ const result = await this.backupService.create(name);
29
+
30
+ // Try to compress the backup
31
+ const compressed = await this.compressBackup(result.path);
32
+
33
+ this.logger.complete(`Backup '${result.name}' created successfully`, {
34
+ files: result.totalFiles,
35
+ size: FileSystemUtils.formatSize(result.totalSize),
36
+ compressed: !!compressed
37
+ });
38
+
39
+ if (compressed) {
40
+ this.logger.info(`Backup compressed and stored at: ${compressed.path}`, {
41
+ compressionRatio: compressed.size / result.totalSize
42
+ });
43
+ } else {
44
+ this.logger.info(`Backup stored at: ${result.path}`);
45
+ }
46
+
47
+ return {
48
+ success: true,
49
+ name: result.name,
50
+ path: compressed ? compressed.path : result.path,
51
+ metadata: result.metadata
52
+ };
53
+
54
+ } catch (error) {
55
+ return this.handleError(error);
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Try to compress a backup using tar
61
+ */
62
+ async compressBackup(backupPath) {
63
+ try {
64
+ const backupName = require('path').basename(backupPath);
65
+ const tarPath = `${backupPath}.tar.gz`;
66
+
67
+ execSync(`tar -czf "${tarPath}" -C "${this.config.backupsDir}" "${backupName}"`, {
68
+ encoding: 'utf8',
69
+ stdio: 'pipe'
70
+ });
71
+
72
+ // Remove uncompressed backup
73
+ FileSystemUtils.remove(backupPath);
74
+
75
+ const compressedSize = FileSystemUtils.getStats(tarPath).size;
76
+ return {
77
+ path: tarPath,
78
+ size: compressedSize
79
+ };
80
+ } catch (error) {
81
+ // Compression failed, keep uncompressed backup
82
+ return null;
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Restore from a backup
88
+ */
89
+ async restore(backupName) {
90
+ this.logger.step(`Restoring from backup: ${backupName}`, { backupName });
91
+
92
+ try {
93
+ // Create undo backup first
94
+ this.logger.info('Creating undo backup for safety');
95
+ const undoBackup = await this.backup('undo-before-restore');
96
+
97
+ if (!undoBackup.success) {
98
+ this.logger.warn('Could not create undo backup, continuing anyway', {
99
+ risk: 'restore cannot be undone'
100
+ });
101
+ }
102
+
103
+ // Perform restore using service
104
+ const result = await this.restoreService.restore(backupName);
105
+
106
+ this.logger.complete(`Restore completed successfully`, {
107
+ restoredCount: result.restoredCount,
108
+ hasUndo: undoBackup.success
109
+ });
110
+
111
+ if (undoBackup.success) {
112
+ this.logger.info(`To undo this restore, run: claude-commands restore ${undoBackup.name}`, {
113
+ undoCommand: `claude-commands restore ${undoBackup.name}`
114
+ });
115
+ }
116
+
117
+ return {
118
+ success: true,
119
+ restoredCount: result.restoredCount,
120
+ undoBackup: undoBackup.success ? undoBackup.name : null
121
+ };
122
+
123
+ } catch (error) {
124
+ return this.handleError(error);
125
+ }
126
+ }
127
+
128
+ /**
129
+ * List available backups
130
+ */
131
+ async listBackups() {
132
+ try {
133
+ return await this.listService.display();
134
+ } catch (error) {
135
+ return this.handleError(error);
136
+ }
137
+ }
138
+ }
139
+
140
+ module.exports = BackupRestoreCommand;
@@ -0,0 +1,252 @@
1
+ /**
2
+ * Base Command Pattern
3
+ * Abstract base class for all CLI commands with standardized error handling
4
+ */
5
+
6
+ const ClaudePathConfig = require('../utils/claude-path-config');
7
+ const FileSystemUtils = require('../utils/file-system-utils');
8
+ const LoggerService = require('../services/logger-service');
9
+
10
+ class BaseCommand {
11
+ constructor(config = null, logger = null) {
12
+ this.config = config || new ClaudePathConfig();
13
+ this.logger = logger || new LoggerService();
14
+ this.startTime = null;
15
+ this.metrics = {
16
+ filesProcessed: 0,
17
+ operationsPerformed: 0,
18
+ errorsEncountered: 0
19
+ };
20
+
21
+ // Set logger context for this command
22
+ this.logger.setContext({
23
+ command: this.constructor.name,
24
+ timestamp: new Date().toISOString()
25
+ });
26
+ }
27
+
28
+ /**
29
+ * Main execution method with standardized error handling
30
+ */
31
+ async execute(options = {}) {
32
+ this.startTime = Date.now();
33
+
34
+ try {
35
+ // Pre-execution validation
36
+ const preValidation = await this.preValidate(options);
37
+ if (!preValidation.success) {
38
+ return this.createFailureResult(preValidation.error, preValidation);
39
+ }
40
+
41
+ // Execute the main command logic
42
+ const result = await this.run(options);
43
+
44
+ // Post-execution cleanup
45
+ await this.postExecute(result, options);
46
+
47
+ return this.createSuccessResult(result);
48
+
49
+ } catch (error) {
50
+ this.metrics.errorsEncountered++;
51
+ return this.handleError(error, options);
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Abstract method - must be implemented by subclasses
57
+ */
58
+ async run(options) {
59
+ throw new Error('Subclasses must implement the run() method');
60
+ }
61
+
62
+ /**
63
+ * Pre-execution validation hook
64
+ */
65
+ async preValidate(options) {
66
+ // Default validation - can be overridden
67
+ return { success: true };
68
+ }
69
+
70
+ /**
71
+ * Post-execution cleanup hook
72
+ */
73
+ async postExecute(result, options) {
74
+ // Default post-execution - can be overridden
75
+ const duration = this.getDuration();
76
+
77
+ if (process.env.NODE_ENV === 'development' || options.verbose) {
78
+ console.log(`\nšŸ“Š Command completed in ${duration}s`);
79
+ if (this.metrics.filesProcessed > 0) {
80
+ console.log(` Files processed: ${this.metrics.filesProcessed}`);
81
+ }
82
+ if (this.metrics.operationsPerformed > 0) {
83
+ console.log(` Operations: ${this.metrics.operationsPerformed}`);
84
+ }
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Standardized error handling
90
+ */
91
+ handleError(error, options = {}) {
92
+ const errorDetails = this.analyzeError(error);
93
+
94
+ // Log error appropriately
95
+ if (options.verbose || process.env.NODE_ENV === 'development') {
96
+ console.error(`šŸ’„ ${this.constructor.name} Error Details:`, error);
97
+ } else {
98
+ console.error(`āŒ ${errorDetails.message}`);
99
+ }
100
+
101
+ // Provide helpful guidance
102
+ if (errorDetails.suggestions.length > 0) {
103
+ console.log('\nšŸ’” Suggestions:');
104
+ errorDetails.suggestions.forEach(suggestion => {
105
+ console.log(` • ${suggestion}`);
106
+ });
107
+ }
108
+
109
+ return this.createFailureResult(errorDetails.message, {
110
+ type: errorDetails.type,
111
+ suggestions: errorDetails.suggestions,
112
+ originalError: options.verbose ? error : undefined
113
+ });
114
+ }
115
+
116
+ /**
117
+ * Analyze error type and generate helpful suggestions
118
+ */
119
+ analyzeError(error) {
120
+ const message = error.message || 'Unknown error occurred';
121
+ const suggestions = [];
122
+ let type = 'general';
123
+
124
+ // File system errors
125
+ if (message.includes('ENOENT') || message.includes('no such file')) {
126
+ type = 'file_not_found';
127
+ suggestions.push('Check that the file or directory exists');
128
+ suggestions.push('Verify you have the correct path');
129
+ } else if (message.includes('EACCES') || message.includes('permission denied')) {
130
+ type = 'permission_error';
131
+ suggestions.push('Check file/directory permissions');
132
+ suggestions.push('Try running with appropriate permissions');
133
+ } else if (message.includes('ENOTDIR') || message.includes('not a directory')) {
134
+ type = 'path_error';
135
+ suggestions.push('Verify the path is correct');
136
+ suggestions.push('Check that parent directories exist');
137
+ }
138
+ // Network errors
139
+ else if (message.includes('ECONNREFUSED') || message.includes('network')) {
140
+ type = 'network_error';
141
+ suggestions.push('Check your internet connection');
142
+ suggestions.push('Verify proxy settings if applicable');
143
+ }
144
+ // Git errors
145
+ else if (message.includes('git') || message.includes('Not a git repository')) {
146
+ type = 'git_error';
147
+ suggestions.push('Ensure you are in a git repository');
148
+ suggestions.push('Run "git init" if needed');
149
+ }
150
+ // Claude Code errors
151
+ else if (message.includes('claude') || message.includes('settings')) {
152
+ type = 'claude_error';
153
+ suggestions.push('Run "claude-commands verify" to check installation');
154
+ suggestions.push('Try "claude-commands setup" to reconfigure');
155
+ }
156
+
157
+ return {
158
+ message: this.sanitizeErrorMessage(message),
159
+ type,
160
+ suggestions
161
+ };
162
+ }
163
+
164
+ /**
165
+ * Sanitize error messages for user consumption
166
+ */
167
+ sanitizeErrorMessage(message) {
168
+ // Remove internal paths and stack traces
169
+ return message
170
+ .replace(/\/Users\/[^\/]+\/[^\s]+/g, '~/<path>')
171
+ .replace(/\s+at\s+.*/g, '')
172
+ .replace(/Error:\s*/g, '')
173
+ .trim();
174
+ }
175
+
176
+ /**
177
+ * Create standardized success result
178
+ */
179
+ createSuccessResult(data = {}) {
180
+ return {
181
+ success: true,
182
+ duration: this.getDuration(),
183
+ metrics: { ...this.metrics },
184
+ timestamp: new Date().toISOString(),
185
+ ...data
186
+ };
187
+ }
188
+
189
+ /**
190
+ * Create standardized failure result
191
+ */
192
+ createFailureResult(message, data = {}) {
193
+ return {
194
+ success: false,
195
+ error: message,
196
+ duration: this.getDuration(),
197
+ metrics: { ...this.metrics },
198
+ timestamp: new Date().toISOString(),
199
+ ...data
200
+ };
201
+ }
202
+
203
+ /**
204
+ * Get execution duration in seconds
205
+ */
206
+ getDuration() {
207
+ if (!this.startTime) return 0;
208
+ return ((Date.now() - this.startTime) / 1000).toFixed(2);
209
+ }
210
+
211
+ /**
212
+ * Helper method to ensure directories exist
213
+ */
214
+ ensureDirectoryExists(dirPath) {
215
+ FileSystemUtils.ensureDirectory(dirPath);
216
+ this.metrics.operationsPerformed++;
217
+ }
218
+
219
+ /**
220
+ * Helper method to safely read files
221
+ */
222
+ safeReadFile(filePath) {
223
+ const content = FileSystemUtils.readFile(filePath);
224
+ if (content !== null) {
225
+ this.metrics.filesProcessed++;
226
+ }
227
+ return content;
228
+ }
229
+
230
+ /**
231
+ * Helper method to safely write files
232
+ */
233
+ safeWriteFile(filePath, content, mode = 0o644) {
234
+ const success = FileSystemUtils.writeFile(filePath, content, mode);
235
+ if (success) {
236
+ this.metrics.filesProcessed++;
237
+ this.metrics.operationsPerformed++;
238
+ }
239
+ return success;
240
+ }
241
+
242
+ /**
243
+ * Display progress if verbose mode enabled
244
+ */
245
+ showProgress(message, options = {}) {
246
+ if (options.verbose || process.env.NODE_ENV === 'development') {
247
+ console.log(`šŸ”„ ${message}`);
248
+ }
249
+ }
250
+ }
251
+
252
+ module.exports = BaseCommand;