@paulduvall/claude-dev-toolkit 0.0.1-alpha.1
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 +254 -0
- package/bin/claude-commands +132 -0
- package/lib/claude-code-compatibility.js +545 -0
- package/lib/command-selector.js +245 -0
- package/lib/config.js +182 -0
- package/lib/context-utils.js +80 -0
- package/lib/dependency-validator.js +354 -0
- package/lib/error-factory.js +394 -0
- package/lib/error-handler-utils.js +432 -0
- package/lib/error-recovery-system.js +563 -0
- package/lib/failure-recovery-installer.js +370 -0
- package/lib/hook-installer-core.js +330 -0
- package/lib/hook-installer.js +187 -0
- package/lib/hook-metadata-service.js +352 -0
- package/lib/hook-validator.js +358 -0
- package/lib/installation-configuration.js +380 -0
- package/lib/installation-instruction-generator.js +564 -0
- package/lib/installer.js +68 -0
- package/lib/package-manager-service.js +270 -0
- package/lib/permission-error-handler.js +543 -0
- package/lib/platform-utils.js +491 -0
- package/lib/setup-wizard-ui.js +245 -0
- package/lib/setup-wizard.js +355 -0
- package/lib/system-requirements-checker.js +558 -0
- package/lib/utils.js +15 -0
- package/lib/validation-utils.js +320 -0
- package/lib/version-validator-service.js +326 -0
- package/package.json +73 -0
- package/scripts/postinstall.js +182 -0
- package/scripts/validate.js +94 -0
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Handler Utilities
|
|
3
|
+
*
|
|
4
|
+
* Shared error handling utilities to eliminate duplication and provide
|
|
5
|
+
* consistent error handling across the application.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Standardized error creation and formatting
|
|
9
|
+
* - Error categorization and classification
|
|
10
|
+
* - Recovery suggestion generation
|
|
11
|
+
* - Error context enhancement
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
class ErrorHandlerUtils {
|
|
15
|
+
constructor() {
|
|
16
|
+
this.config = {
|
|
17
|
+
errorCategories: {
|
|
18
|
+
PERMISSION: 'permission',
|
|
19
|
+
VALIDATION: 'validation',
|
|
20
|
+
NETWORK: 'network',
|
|
21
|
+
SYSTEM: 'system',
|
|
22
|
+
DEPENDENCY: 'dependency',
|
|
23
|
+
CONFIGURATION: 'configuration'
|
|
24
|
+
},
|
|
25
|
+
errorCodes: {
|
|
26
|
+
// Permission errors
|
|
27
|
+
EACCES: 'EACCES',
|
|
28
|
+
EPERM: 'EPERM',
|
|
29
|
+
ENOENT: 'ENOENT',
|
|
30
|
+
|
|
31
|
+
// Validation errors
|
|
32
|
+
VALIDATION_ERROR: 'VALIDATION_ERROR',
|
|
33
|
+
INVALID_INPUT: 'INVALID_INPUT',
|
|
34
|
+
INVALID_CONFIGURATION: 'INVALID_CONFIGURATION',
|
|
35
|
+
MISSING_REQUIRED_FIELD: 'MISSING_REQUIRED_FIELD',
|
|
36
|
+
|
|
37
|
+
// Dependency errors
|
|
38
|
+
NOT_FOUND: 'NOT_FOUND',
|
|
39
|
+
VERSION_MISMATCH: 'VERSION_MISMATCH',
|
|
40
|
+
DEPENDENCY_CONFLICT: 'DEPENDENCY_CONFLICT',
|
|
41
|
+
|
|
42
|
+
// Network errors
|
|
43
|
+
ENOTFOUND: 'ENOTFOUND',
|
|
44
|
+
TIMEOUT: 'TIMEOUT',
|
|
45
|
+
ECONNREFUSED: 'ECONNREFUSED',
|
|
46
|
+
|
|
47
|
+
// System errors
|
|
48
|
+
SYSTEM_FAILURE: 'SYSTEM_FAILURE',
|
|
49
|
+
CORRUPTION: 'CORRUPTION',
|
|
50
|
+
INSUFFICIENT_RESOURCES: 'INSUFFICIENT_RESOURCES',
|
|
51
|
+
|
|
52
|
+
// Installation errors
|
|
53
|
+
INSTALLATION_FAILED: 'INSTALLATION_FAILED',
|
|
54
|
+
BACKUP_FAILED: 'BACKUP_FAILED',
|
|
55
|
+
ROLLBACK_FAILED: 'ROLLBACK_FAILED',
|
|
56
|
+
|
|
57
|
+
// Generic errors
|
|
58
|
+
UNKNOWN_ERROR: 'UNKNOWN_ERROR',
|
|
59
|
+
OPERATION_FAILED: 'OPERATION_FAILED',
|
|
60
|
+
STRING_ERROR: 'STRING_ERROR'
|
|
61
|
+
},
|
|
62
|
+
recoverySuggestionTemplates: this._createRecoverySuggestionTemplates()
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Create recovery suggestion templates
|
|
68
|
+
* @returns {Object} Recovery suggestion templates
|
|
69
|
+
* @private
|
|
70
|
+
*/
|
|
71
|
+
_createRecoverySuggestionTemplates() {
|
|
72
|
+
return {
|
|
73
|
+
permission: {
|
|
74
|
+
immediate: [
|
|
75
|
+
'Try: Check file and directory permissions',
|
|
76
|
+
'Solution: Run command with elevated privileges'
|
|
77
|
+
],
|
|
78
|
+
alternative: [
|
|
79
|
+
'Use alternative installation location',
|
|
80
|
+
'Install to user directory instead of system'
|
|
81
|
+
],
|
|
82
|
+
troubleshooting: [
|
|
83
|
+
'Check system logs for permission issues',
|
|
84
|
+
'Verify user has necessary access rights',
|
|
85
|
+
'Contact system administrator if needed'
|
|
86
|
+
]
|
|
87
|
+
},
|
|
88
|
+
dependency: {
|
|
89
|
+
immediate: [
|
|
90
|
+
'Try: Install missing dependency',
|
|
91
|
+
'Solution: Update system package manager'
|
|
92
|
+
],
|
|
93
|
+
alternative: [
|
|
94
|
+
'Use alternative package manager',
|
|
95
|
+
'Download and install manually'
|
|
96
|
+
],
|
|
97
|
+
troubleshooting: [
|
|
98
|
+
'Check internet connectivity',
|
|
99
|
+
'Verify package repository configuration',
|
|
100
|
+
'Clear package manager cache'
|
|
101
|
+
]
|
|
102
|
+
},
|
|
103
|
+
validation: {
|
|
104
|
+
immediate: [
|
|
105
|
+
'Try: Verify input parameters',
|
|
106
|
+
'Solution: Check data format and types'
|
|
107
|
+
],
|
|
108
|
+
alternative: [
|
|
109
|
+
'Use default values where applicable',
|
|
110
|
+
'Regenerate configuration if corrupted'
|
|
111
|
+
],
|
|
112
|
+
troubleshooting: [
|
|
113
|
+
'Enable debug mode for detailed errors',
|
|
114
|
+
'Check application logs',
|
|
115
|
+
'Review input validation requirements'
|
|
116
|
+
]
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Create enhanced error object with additional context
|
|
123
|
+
* @param {Error|Object} originalError - Original error or error-like object
|
|
124
|
+
* @param {Object} context - Additional context information
|
|
125
|
+
* @returns {Object} Enhanced error object
|
|
126
|
+
*/
|
|
127
|
+
createEnhancedError(originalError, context = {}) {
|
|
128
|
+
const baseError = this._extractBaseErrorInfo(originalError);
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
...baseError,
|
|
132
|
+
category: this._categorizeError(baseError),
|
|
133
|
+
context: context,
|
|
134
|
+
timestamp: new Date().toISOString(),
|
|
135
|
+
handled: false,
|
|
136
|
+
recoverable: this._isRecoverableError(baseError),
|
|
137
|
+
severity: this._determineSeverity(baseError, context)
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Extract base error information from various error types
|
|
143
|
+
* @param {Error|Object} error - Error to extract from
|
|
144
|
+
* @returns {Object} Base error information
|
|
145
|
+
* @private
|
|
146
|
+
*/
|
|
147
|
+
_extractBaseErrorInfo(error) {
|
|
148
|
+
if (!error) {
|
|
149
|
+
return {
|
|
150
|
+
code: this.config.errorCodes.INVALID_INPUT,
|
|
151
|
+
message: 'Unknown error occurred',
|
|
152
|
+
type: 'unknown'
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Handle Error objects
|
|
157
|
+
if (error instanceof Error) {
|
|
158
|
+
return {
|
|
159
|
+
code: error.code || 'UNKNOWN_ERROR',
|
|
160
|
+
message: error.message,
|
|
161
|
+
type: error.constructor.name,
|
|
162
|
+
stack: error.stack,
|
|
163
|
+
path: error.path,
|
|
164
|
+
errno: error.errno
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Handle error-like objects
|
|
169
|
+
if (typeof error === 'object') {
|
|
170
|
+
return {
|
|
171
|
+
code: error.code || 'UNKNOWN_ERROR',
|
|
172
|
+
message: error.message || 'Unknown error occurred',
|
|
173
|
+
type: error.type || 'object',
|
|
174
|
+
...error
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Handle string errors
|
|
179
|
+
return {
|
|
180
|
+
code: 'STRING_ERROR',
|
|
181
|
+
message: String(error),
|
|
182
|
+
type: 'string'
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Categorize error based on code and type
|
|
188
|
+
* @param {Object} errorInfo - Error information
|
|
189
|
+
* @returns {string} Error category
|
|
190
|
+
* @private
|
|
191
|
+
*/
|
|
192
|
+
_categorizeError(errorInfo) {
|
|
193
|
+
const code = errorInfo.code;
|
|
194
|
+
|
|
195
|
+
// Permission errors
|
|
196
|
+
if (['EACCES', 'EPERM', 'ENOENT'].includes(code)) {
|
|
197
|
+
return this.config.errorCategories.PERMISSION;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Network errors
|
|
201
|
+
if (['ENOTFOUND', 'TIMEOUT', 'ECONNREFUSED'].includes(code)) {
|
|
202
|
+
return this.config.errorCategories.NETWORK;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Validation errors
|
|
206
|
+
if (['VALIDATION_ERROR', 'INVALID_INPUT'].includes(code)) {
|
|
207
|
+
return this.config.errorCategories.VALIDATION;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Dependency errors
|
|
211
|
+
if (['NOT_FOUND', 'VERSION_MISMATCH'].includes(code)) {
|
|
212
|
+
return this.config.errorCategories.DEPENDENCY;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return this.config.errorCategories.SYSTEM;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Determine if error is recoverable
|
|
220
|
+
* @param {Object} errorInfo - Error information
|
|
221
|
+
* @returns {boolean} True if error is recoverable
|
|
222
|
+
* @private
|
|
223
|
+
*/
|
|
224
|
+
_isRecoverableError(errorInfo) {
|
|
225
|
+
const recoverableCodes = [
|
|
226
|
+
'EACCES', 'EPERM', 'NOT_FOUND', 'VERSION_MISMATCH',
|
|
227
|
+
'TIMEOUT', 'VALIDATION_ERROR'
|
|
228
|
+
];
|
|
229
|
+
|
|
230
|
+
return recoverableCodes.includes(errorInfo.code);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Determine error severity
|
|
235
|
+
* @param {Object} errorInfo - Error information
|
|
236
|
+
* @param {Object} context - Error context
|
|
237
|
+
* @returns {string} Severity level (low, medium, high, critical)
|
|
238
|
+
* @private
|
|
239
|
+
*/
|
|
240
|
+
_determineSeverity(errorInfo, context) {
|
|
241
|
+
// Critical errors that prevent core functionality
|
|
242
|
+
if (['SYSTEM_FAILURE', 'CORRUPTION'].includes(errorInfo.code)) {
|
|
243
|
+
return 'critical';
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// High severity for system-level issues
|
|
247
|
+
if (context.scope === 'system' || ['EPERM', 'EACCES'].includes(errorInfo.code)) {
|
|
248
|
+
return 'high';
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Medium severity for dependency and validation issues
|
|
252
|
+
if (['NOT_FOUND', 'VERSION_MISMATCH', 'VALIDATION_ERROR'].includes(errorInfo.code)) {
|
|
253
|
+
return 'medium';
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Low severity for non-blocking issues
|
|
257
|
+
return 'low';
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Generate recovery suggestions for error
|
|
262
|
+
* @param {Object} enhancedError - Enhanced error object
|
|
263
|
+
* @returns {Object} Recovery suggestions
|
|
264
|
+
*/
|
|
265
|
+
generateRecoverySuggestions(enhancedError) {
|
|
266
|
+
const category = enhancedError.category;
|
|
267
|
+
const template = this.config.recoverySuggestionTemplates[category] ||
|
|
268
|
+
this.config.recoverySuggestionTemplates.validation;
|
|
269
|
+
|
|
270
|
+
const suggestions = {
|
|
271
|
+
immediate: [...template.immediate],
|
|
272
|
+
alternative: [...template.alternative],
|
|
273
|
+
troubleshooting: [...template.troubleshooting]
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
// Add context-specific suggestions
|
|
277
|
+
this._addContextSpecificSuggestions(suggestions, enhancedError);
|
|
278
|
+
|
|
279
|
+
return suggestions;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Add context-specific suggestions to recovery options
|
|
284
|
+
* @param {Object} suggestions - Suggestions object to modify
|
|
285
|
+
* @param {Object} enhancedError - Enhanced error object
|
|
286
|
+
* @private
|
|
287
|
+
*/
|
|
288
|
+
_addContextSpecificSuggestions(suggestions, enhancedError) {
|
|
289
|
+
const { context, code } = enhancedError;
|
|
290
|
+
|
|
291
|
+
// Add path-specific suggestions
|
|
292
|
+
if (enhancedError.path) {
|
|
293
|
+
suggestions.immediate.push(`Check permissions for path: ${enhancedError.path}`);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Add operation-specific suggestions
|
|
297
|
+
if (context.operation) {
|
|
298
|
+
suggestions.alternative.push(`Retry ${context.operation} with different parameters`);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Add dependency-specific suggestions
|
|
302
|
+
if (context.dependency) {
|
|
303
|
+
suggestions.immediate.push(`Install dependency: ${context.dependency}`);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Add command-specific suggestions
|
|
307
|
+
if (context.command) {
|
|
308
|
+
suggestions.troubleshooting.push(`Debug command: ${context.command}`);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Create error handling result object
|
|
314
|
+
* @param {Object} enhancedError - Enhanced error object
|
|
315
|
+
* @param {boolean} handled - Whether error was handled
|
|
316
|
+
* @param {Object} recovery - Recovery information
|
|
317
|
+
* @returns {Object} Error handling result
|
|
318
|
+
*/
|
|
319
|
+
createErrorHandlingResult(enhancedError, handled = false, recovery = null) {
|
|
320
|
+
return {
|
|
321
|
+
error: enhancedError,
|
|
322
|
+
handled,
|
|
323
|
+
recovery,
|
|
324
|
+
suggestions: this.generateRecoverySuggestions(enhancedError),
|
|
325
|
+
timestamp: new Date().toISOString(),
|
|
326
|
+
actionable: enhancedError.recoverable,
|
|
327
|
+
contextAware: Boolean(enhancedError.context && Object.keys(enhancedError.context).length > 0)
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Format error for user display
|
|
333
|
+
* @param {Object} enhancedError - Enhanced error object
|
|
334
|
+
* @param {Object} options - Formatting options
|
|
335
|
+
* @returns {string} Formatted error message
|
|
336
|
+
*/
|
|
337
|
+
formatErrorForDisplay(enhancedError, options = {}) {
|
|
338
|
+
const {
|
|
339
|
+
includeCode = true,
|
|
340
|
+
includeContext = true,
|
|
341
|
+
includeStack = false,
|
|
342
|
+
maxLength = 200
|
|
343
|
+
} = options;
|
|
344
|
+
|
|
345
|
+
let message = enhancedError.message;
|
|
346
|
+
|
|
347
|
+
if (includeCode && enhancedError.code) {
|
|
348
|
+
message = `[${enhancedError.code}] ${message}`;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (includeContext && enhancedError.context && enhancedError.context.operation) {
|
|
352
|
+
message = `${message} (during ${enhancedError.context.operation})`;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (includeStack && enhancedError.stack) {
|
|
356
|
+
message += `\n\nStack trace:\n${enhancedError.stack}`;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Truncate if too long
|
|
360
|
+
if (message.length > maxLength) {
|
|
361
|
+
message = message.substring(0, maxLength - 3) + '...';
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return message;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Check if error matches specific criteria
|
|
369
|
+
* @param {Object} enhancedError - Enhanced error object
|
|
370
|
+
* @param {Object} criteria - Matching criteria
|
|
371
|
+
* @returns {boolean} True if error matches criteria
|
|
372
|
+
*/
|
|
373
|
+
matchesErrorCriteria(enhancedError, criteria) {
|
|
374
|
+
const {
|
|
375
|
+
code,
|
|
376
|
+
category,
|
|
377
|
+
severity,
|
|
378
|
+
recoverable,
|
|
379
|
+
contextOperation
|
|
380
|
+
} = criteria;
|
|
381
|
+
|
|
382
|
+
if (code && enhancedError.code !== code) return false;
|
|
383
|
+
if (category && enhancedError.category !== category) return false;
|
|
384
|
+
if (severity && enhancedError.severity !== severity) return false;
|
|
385
|
+
if (recoverable !== undefined && enhancedError.recoverable !== recoverable) return false;
|
|
386
|
+
if (contextOperation && enhancedError.context?.operation !== contextOperation) return false;
|
|
387
|
+
|
|
388
|
+
return true;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Log error with context information
|
|
393
|
+
* @param {Object} enhancedError - Enhanced error object
|
|
394
|
+
* @param {Object} context - Additional logging context
|
|
395
|
+
*/
|
|
396
|
+
logError(enhancedError, context = {}) {
|
|
397
|
+
const logLevel = this._getLogLevel(enhancedError.severity);
|
|
398
|
+
const logMessage = this.formatErrorForDisplay(enhancedError, {
|
|
399
|
+
includeCode: true,
|
|
400
|
+
includeContext: true,
|
|
401
|
+
includeStack: false
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
console[logLevel](`[${enhancedError.category}] ${logMessage}`);
|
|
405
|
+
|
|
406
|
+
if (context.attempt) {
|
|
407
|
+
console.warn(` Attempt ${context.attempt}/${context.maxAttempts || 'unknown'}`);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (context.operationId) {
|
|
411
|
+
console.debug(` Operation ID: ${context.operationId}`);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Get appropriate console log level for error severity
|
|
417
|
+
* @param {string} severity - Error severity level
|
|
418
|
+
* @returns {string} Console method name
|
|
419
|
+
* @private
|
|
420
|
+
*/
|
|
421
|
+
_getLogLevel(severity) {
|
|
422
|
+
switch (severity) {
|
|
423
|
+
case 'critical': return 'error';
|
|
424
|
+
case 'high': return 'error';
|
|
425
|
+
case 'medium': return 'warn';
|
|
426
|
+
case 'low': return 'info';
|
|
427
|
+
default: return 'log';
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
module.exports = ErrorHandlerUtils;
|