UNPKG

@hugsylabs/hugsy

Version:

🐧 Hugsy - Configuration management for Claude Code. Transform complex settings into simple, shareable team standards.

250 lines (249 loc) 9.18 kB
/** * Error reporter for user-friendly error messages */ export var ErrorCode; (function (ErrorCode) { ErrorCode["CIRCULAR_DEPENDENCY"] = "CIRCULAR_DEPENDENCY"; ErrorCode["INVALID_PERMISSION"] = "INVALID_PERMISSION"; ErrorCode["PLUGIN_LOAD_ERROR"] = "PLUGIN_LOAD_ERROR"; ErrorCode["PRESET_NOT_FOUND"] = "PRESET_NOT_FOUND"; ErrorCode["CONFIG_VALIDATION"] = "CONFIG_VALIDATION"; ErrorCode["FILE_WRITE_ERROR"] = "FILE_WRITE_ERROR"; ErrorCode["HOOK_EXECUTION_ERROR"] = "HOOK_EXECUTION_ERROR"; })(ErrorCode || (ErrorCode = {})); export class ErrorReporter { /** * Format error for display */ static format(error) { const lines = []; // Error header with emoji based on type const emoji = this.getErrorEmoji(error.code); lines.push(`${emoji} ${this.getErrorTitle(error.code)}`); lines.push(''); // Main error message lines.push(error.message); // Additional details if provided if (error.details) { lines.push(''); lines.push('Details:'); const detailLines = error.details.split('\n'); detailLines.forEach(line => { lines.push(` ${line}`); }); } // Context information if (error.context && Object.keys(error.context).length > 0) { lines.push(''); lines.push('Context:'); for (const [key, value] of Object.entries(error.context)) { lines.push(` ${key}: ${value}`); } } // Suggestion for fixing if (error.suggestion) { lines.push(''); lines.push('💡 How to fix:'); const suggestionLines = error.suggestion.split('\n'); suggestionLines.forEach(line => { lines.push(` ${line}`); }); } return lines.join('\n'); } /** * Create a circular dependency error */ static circularDependency(cycle) { return { code: ErrorCode.CIRCULAR_DEPENDENCY, message: `Circular dependency detected: ${cycle.join(' -> ')}`, details: 'Your configuration has presets that depend on each other in a circular way.', suggestion: `Remove the extends reference from "${cycle[cycle.length - 2]}" to "${cycle[cycle.length - 1]}" Or restructure your presets to avoid circular dependencies.`, context: { cycleLength: cycle.length - 1, involvedPresets: cycle.slice(0, -1).join(', ') } }; } /** * Create an invalid permission error */ static invalidPermission(permission, reason) { return { code: ErrorCode.INVALID_PERMISSION, message: `Invalid permission format: "${permission}"`, details: reason, suggestion: `Permissions must match the pattern: Tool or Tool(pattern) Examples: - Read(**) - Write(**/test/**) - Bash(npm test) Make sure: - Tool names start with uppercase letter - Patterns are properly enclosed in parentheses - No spaces between Tool and parentheses`, context: { invalidValue: permission } }; } /** * Create a plugin load error */ static pluginLoadError(pluginPath, error) { const isLocalPath = pluginPath.startsWith('./') || pluginPath.startsWith('../'); return { code: ErrorCode.PLUGIN_LOAD_ERROR, message: `Failed to load plugin: ${pluginPath}`, details: error.message, suggestion: isLocalPath ? `Check that the file exists and exports a valid plugin object: - Verify the file path is correct - Ensure the file exports a default object or named export - Check for syntax errors in the plugin file` : `Ensure the npm package is installed: - Run: npm install ${pluginPath} - Check package.json dependencies`, context: { pluginPath, errorType: error.name } }; } /** * Create a preset not found error */ static presetNotFound(presetName) { const isBuiltin = presetName.startsWith('@hugsy/'); const isLocal = presetName.startsWith('./') || presetName.startsWith('../'); let suggestion = ''; if (isBuiltin) { suggestion = `Built-in preset "${presetName}" not found. Available built-in presets: - @hugsy/recommended - @hugsy/minimal - @hugsy/strict`; } else if (isLocal) { suggestion = `Check that the file exists at the specified path. The path is relative to your project root.`; } else { suggestion = `Install the preset package: npm install ${presetName}`; } return { code: ErrorCode.PRESET_NOT_FOUND, message: `Preset not found: ${presetName}`, suggestion, context: { presetName, type: isBuiltin ? 'builtin' : isLocal ? 'local' : 'npm' } }; } /** * Create a config validation error */ static configValidation(field, issue) { const suggestions = { extends: 'The extends field should be a string or array of preset names.', permissions: 'Permissions should have allow, deny, or ask arrays with valid patterns.', plugins: 'Plugins should be an array of plugin paths or package names.', env: 'Environment variables should be an object with string keys and values.', hooks: 'Hooks should follow the Claude Code hook configuration format.', commands: 'Commands can be an object, array of presets, or command configuration.', }; return { code: ErrorCode.CONFIG_VALIDATION, message: `Configuration validation failed for field: ${field}`, details: issue, suggestion: suggestions[field] || 'Check the documentation for valid configuration options.', context: { field, validationType: 'schema' } }; } /** * Create a file write error */ static fileWriteError(filePath, error) { const isPermission = error.message.includes('EACCES') || error.message.includes('permission'); const isDirNotExist = error.message.includes('ENOENT'); let suggestion = 'Check file system permissions and disk space.'; if (isPermission) { suggestion = `Permission denied. Try: - Running with appropriate permissions - Checking file ownership - Ensuring the directory is writable`; } else if (isDirNotExist) { suggestion = `Directory doesn't exist. The parent directory will be created automatically. If this persists, check the path is correct.`; } return { code: ErrorCode.FILE_WRITE_ERROR, message: `Failed to write file: ${filePath}`, details: error.message, suggestion, context: { filePath, errorCode: error.code ?? 'UNKNOWN' } }; } /** * Create a hook execution error */ static hookExecutionError(hookType, command, error) { return { code: ErrorCode.HOOK_EXECUTION_ERROR, message: `Hook execution failed: ${hookType}`, details: `Command: ${command}\nError: ${error.message}`, suggestion: `Check that the command is valid and executable: - Verify the command exists in PATH - Check for syntax errors in the command - Ensure required dependencies are installed - Try running the command manually to debug`, context: { hookType, command, exitCode: error.code ?? 'UNKNOWN' } }; } /** * Get emoji for error type */ static getErrorEmoji(code) { const emojis = { [ErrorCode.CIRCULAR_DEPENDENCY]: '🔄', [ErrorCode.INVALID_PERMISSION]: '🚫', [ErrorCode.PLUGIN_LOAD_ERROR]: '🔌', [ErrorCode.PRESET_NOT_FOUND]: '📦', [ErrorCode.CONFIG_VALIDATION]: '⚠️', [ErrorCode.FILE_WRITE_ERROR]: '💾', [ErrorCode.HOOK_EXECUTION_ERROR]: '🪝', }; return emojis[code] ?? '❌'; } /** * Get title for error type */ static getErrorTitle(code) { const titles = { [ErrorCode.CIRCULAR_DEPENDENCY]: 'Circular Dependency Error', [ErrorCode.INVALID_PERMISSION]: 'Invalid Permission Format', [ErrorCode.PLUGIN_LOAD_ERROR]: 'Plugin Load Error', [ErrorCode.PRESET_NOT_FOUND]: 'Preset Not Found', [ErrorCode.CONFIG_VALIDATION]: 'Configuration Error', [ErrorCode.FILE_WRITE_ERROR]: 'File Write Error', [ErrorCode.HOOK_EXECUTION_ERROR]: 'Hook Execution Failed', }; return titles[code] ?? 'Error'; } } //# sourceMappingURL=error-reporter.js.map