@paulduvall/claude-dev-toolkit 0.0.1-alpha.7 ā 0.0.1-alpha.8
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.
- package/README.md +9 -9
- package/bin/claude-commands +161 -6
- package/hooks/lib/argument-parser.sh +0 -0
- package/hooks/lib/config-constants.sh +0 -0
- package/hooks/lib/context-manager.sh +0 -0
- package/hooks/lib/error-handler.sh +0 -0
- package/hooks/lib/execution-engine.sh +0 -0
- package/hooks/lib/file-utils.sh +0 -0
- package/hooks/lib/subagent-discovery.sh +0 -0
- package/hooks/lib/subagent-validator.sh +0 -0
- package/lib/backup-restore-command.js +140 -0
- package/lib/base/base-command.js +252 -0
- package/lib/base/command-result.js +184 -0
- package/lib/config/constants.js +255 -0
- package/lib/config.js +48 -6
- package/lib/configure-command.js +428 -0
- package/lib/dependency-validator.js +64 -5
- package/lib/installation-instruction-generator-backup.js +579 -0
- package/lib/installation-instruction-generator.js +213 -495
- package/lib/installer.js +134 -52
- package/lib/services/backup-list-service.js +226 -0
- package/lib/services/backup-service.js +230 -0
- package/lib/services/command-installer-service.js +217 -0
- package/lib/services/logger-service.js +201 -0
- package/lib/services/package-manager-service.js +319 -0
- package/lib/services/platform-instruction-service.js +294 -0
- package/lib/services/recovery-instruction-service.js +348 -0
- package/lib/services/restore-service.js +221 -0
- package/lib/setup-command.js +309 -0
- package/lib/utils/claude-path-config.js +184 -0
- package/lib/utils/file-system-utils.js +152 -0
- package/lib/utils.js +8 -4
- package/lib/verify-command.js +430 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -174,8 +174,8 @@ npm run lint # Code linting
|
|
|
174
174
|
- **Configuration Tests**: Template and setup validation
|
|
175
175
|
|
|
176
176
|
### Architecture
|
|
177
|
-
- **
|
|
178
|
-
- **JavaScript-Based**:
|
|
177
|
+
- **Self-Contained Package**: No dependencies on repository cloning
|
|
178
|
+
- **JavaScript-Based**: Native Node.js integration with comprehensive testing
|
|
179
179
|
- **Modular Design**: Separate installer, config, and validation modules
|
|
180
180
|
- **Cross-Platform**: Works on macOS, Linux, and Windows
|
|
181
181
|
|
|
@@ -212,13 +212,13 @@ npm run validate # Validate package
|
|
|
212
212
|
|
|
213
213
|
### Validation Commands
|
|
214
214
|
```bash
|
|
215
|
-
# Repository validation (from main repo)
|
|
216
|
-
./validate-commands.sh # JavaScript-based validation
|
|
217
|
-
./verify-setup.sh # Complete setup verification
|
|
218
|
-
|
|
219
215
|
# Package validation
|
|
220
216
|
npm run validate # Package structure validation
|
|
221
217
|
npm test # Comprehensive test suite
|
|
218
|
+
|
|
219
|
+
# CLI validation
|
|
220
|
+
claude-commands verify # Complete setup verification
|
|
221
|
+
claude-commands list # Check installed commands
|
|
222
222
|
```
|
|
223
223
|
|
|
224
224
|
## š Documentation
|
|
@@ -252,8 +252,8 @@ npm test
|
|
|
252
252
|
```
|
|
253
253
|
|
|
254
254
|
### Adding Commands
|
|
255
|
-
1. Create command files in
|
|
256
|
-
2. Commands
|
|
255
|
+
1. Create command files in repository `slash-commands/active/` or `slash-commands/experiments/`
|
|
256
|
+
2. Commands are included in NPM package through build process
|
|
257
257
|
3. Validate with `npm run test:commands`
|
|
258
258
|
4. Follow existing patterns and security guidelines
|
|
259
259
|
|
|
@@ -264,7 +264,7 @@ npm test
|
|
|
264
264
|
|
|
265
265
|
## š Recent Updates
|
|
266
266
|
|
|
267
|
-
### Version 0.0.1-alpha.
|
|
267
|
+
### Version 0.0.1-alpha.7
|
|
268
268
|
- ā
**NPM Scoped Package**: Published as `@paulduvall/claude-dev-toolkit`
|
|
269
269
|
- ā
**Configuration Command**: Built-in `config` command for template management
|
|
270
270
|
- ā
**Workflow Reporting**: Comprehensive GitHub Actions reporting
|
package/bin/claude-commands
CHANGED
|
@@ -48,13 +48,25 @@ program
|
|
|
48
48
|
|
|
49
49
|
program
|
|
50
50
|
.command('install')
|
|
51
|
-
.description('Install command sets')
|
|
52
|
-
.option('--active', 'Install
|
|
53
|
-
.option('--experiments', 'Install experimental commands')
|
|
54
|
-
.option('--all', 'Install
|
|
55
|
-
.
|
|
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
|
-
|
|
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,147 @@ 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('update')
|
|
235
|
+
.description('Check for package updates')
|
|
236
|
+
.action(async () => {
|
|
237
|
+
const { version } = require('../package.json');
|
|
238
|
+
console.log('š Checking for updates...\n');
|
|
239
|
+
console.log(`Current version: ${version}`);
|
|
240
|
+
|
|
241
|
+
try {
|
|
242
|
+
const { execSync } = require('child_process');
|
|
243
|
+
const output = execSync('npm view @paulduvall/claude-dev-toolkit version', {
|
|
244
|
+
encoding: 'utf8',
|
|
245
|
+
stdio: 'pipe'
|
|
246
|
+
}).trim();
|
|
247
|
+
|
|
248
|
+
console.log(`Latest version: ${output}`);
|
|
249
|
+
|
|
250
|
+
if (output !== version) {
|
|
251
|
+
console.log('\nš Update available!');
|
|
252
|
+
console.log('\nTo update, run:');
|
|
253
|
+
console.log(' npm update -g @paulduvall/claude-dev-toolkit');
|
|
254
|
+
|
|
255
|
+
// Check for breaking changes in major version
|
|
256
|
+
const currentMajor = parseInt(version.split('.')[0]);
|
|
257
|
+
const latestMajor = parseInt(output.split('.')[0]);
|
|
258
|
+
|
|
259
|
+
if (latestMajor > currentMajor) {
|
|
260
|
+
console.log('\nā ļø Major version update - may contain breaking changes');
|
|
261
|
+
console.log(' Review release notes before updating');
|
|
262
|
+
}
|
|
263
|
+
} else {
|
|
264
|
+
console.log('\nā
You are using the latest version');
|
|
265
|
+
}
|
|
266
|
+
} catch (error) {
|
|
267
|
+
console.error('ā Could not check for updates');
|
|
268
|
+
console.log(' Please check your internet connection');
|
|
269
|
+
process.exit(1);
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
|
|
118
273
|
program.parse(process.argv);
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/hooks/lib/file-utils.sh
CHANGED
|
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;
|