agileflow 2.88.0 → 2.89.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.
@@ -0,0 +1,233 @@
1
+ /**
2
+ * ide-errors.js - Typed Exception Classes for IDE Installers
3
+ *
4
+ * Provides specific error types for common IDE setup failures.
5
+ * These errors carry context about what failed and why,
6
+ * enabling better error handling and user feedback.
7
+ */
8
+
9
+ /**
10
+ * Base error class for IDE-related errors.
11
+ * All IDE errors extend this class.
12
+ */
13
+ class IdeError extends Error {
14
+ /**
15
+ * @param {string} message - Error description
16
+ * @param {string} ideName - Name of the IDE (e.g., 'Claude Code', 'Cursor')
17
+ * @param {Object} [context={}] - Additional context about the error
18
+ */
19
+ constructor(message, ideName, context = {}) {
20
+ super(message);
21
+ this.name = this.constructor.name;
22
+ this.ideName = ideName;
23
+ this.context = context;
24
+ Error.captureStackTrace(this, this.constructor);
25
+ }
26
+
27
+ /**
28
+ * Get a user-friendly description of the error
29
+ * @returns {string}
30
+ */
31
+ getUserMessage() {
32
+ return `${this.ideName}: ${this.message}`;
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Thrown when IDE configuration directory is not found.
38
+ * Example: .claude directory doesn't exist, .cursor not found
39
+ */
40
+ class IdeConfigNotFoundError extends IdeError {
41
+ /**
42
+ * @param {string} ideName - Name of the IDE
43
+ * @param {string} configPath - Expected config directory path
44
+ * @param {Object} [context={}] - Additional context
45
+ */
46
+ constructor(ideName, configPath, context = {}) {
47
+ super(`Configuration directory not found: ${configPath}`, ideName, {
48
+ configPath,
49
+ ...context,
50
+ });
51
+ this.configPath = configPath;
52
+ }
53
+
54
+ /**
55
+ * Get suggested action to fix the error
56
+ * @returns {string}
57
+ */
58
+ getSuggestedAction() {
59
+ return `Create the ${this.configPath} directory or run the IDE at least once to initialize it.`;
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Thrown when command installation fails.
65
+ * Example: Failed to copy command files, directory creation failed
66
+ */
67
+ class CommandInstallationError extends IdeError {
68
+ /**
69
+ * @param {string} ideName - Name of the IDE
70
+ * @param {string} commandName - Name of the command being installed
71
+ * @param {string} reason - Why the installation failed
72
+ * @param {Object} [context={}] - Additional context
73
+ */
74
+ constructor(ideName, commandName, reason, context = {}) {
75
+ super(`Failed to install command '${commandName}': ${reason}`, ideName, {
76
+ commandName,
77
+ reason,
78
+ ...context,
79
+ });
80
+ this.commandName = commandName;
81
+ this.reason = reason;
82
+ }
83
+
84
+ /**
85
+ * Get suggested action to fix the error
86
+ * @returns {string}
87
+ */
88
+ getSuggestedAction() {
89
+ if (this.reason.includes('permission')) {
90
+ return `Check file permissions for the installation directory.`;
91
+ }
92
+ if (this.reason.includes('disk')) {
93
+ return `Free up disk space and try again.`;
94
+ }
95
+ return `Try running the installation again or check the source files.`;
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Thrown when a file operation fails due to permission issues.
101
+ * Example: Cannot write to config directory, read access denied
102
+ */
103
+ class FilePermissionError extends IdeError {
104
+ /**
105
+ * @param {string} ideName - Name of the IDE
106
+ * @param {string} filePath - Path to the file/directory
107
+ * @param {string} operation - Operation that failed ('read', 'write', 'delete')
108
+ * @param {Object} [context={}] - Additional context
109
+ */
110
+ constructor(ideName, filePath, operation, context = {}) {
111
+ super(`Permission denied: cannot ${operation} '${filePath}'`, ideName, {
112
+ filePath,
113
+ operation,
114
+ ...context,
115
+ });
116
+ this.filePath = filePath;
117
+ this.operation = operation;
118
+ }
119
+
120
+ /**
121
+ * Get suggested action to fix the error
122
+ * @returns {string}
123
+ */
124
+ getSuggestedAction() {
125
+ return `Check permissions on '${this.filePath}' or run with appropriate privileges.`;
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Thrown when content injection fails.
131
+ * Example: Template placeholder not found, dynamic content generation failed
132
+ */
133
+ class ContentInjectionError extends IdeError {
134
+ /**
135
+ * @param {string} ideName - Name of the IDE
136
+ * @param {string} templateFile - Path to the template file
137
+ * @param {string} reason - Why injection failed
138
+ * @param {Object} [context={}] - Additional context
139
+ */
140
+ constructor(ideName, templateFile, reason, context = {}) {
141
+ super(`Content injection failed for '${templateFile}': ${reason}`, ideName, {
142
+ templateFile,
143
+ reason,
144
+ ...context,
145
+ });
146
+ this.templateFile = templateFile;
147
+ this.reason = reason;
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Thrown when cleanup operation fails.
153
+ * Example: Cannot remove old installation, locked files
154
+ */
155
+ class CleanupError extends IdeError {
156
+ /**
157
+ * @param {string} ideName - Name of the IDE
158
+ * @param {string} targetPath - Path being cleaned up
159
+ * @param {string} reason - Why cleanup failed
160
+ * @param {Object} [context={}] - Additional context
161
+ */
162
+ constructor(ideName, targetPath, reason, context = {}) {
163
+ super(`Cleanup failed for '${targetPath}': ${reason}`, ideName, {
164
+ targetPath,
165
+ reason,
166
+ ...context,
167
+ });
168
+ this.targetPath = targetPath;
169
+ this.reason = reason;
170
+ }
171
+ }
172
+
173
+ /**
174
+ * Thrown when IDE detection fails or returns unexpected results.
175
+ * Example: Multiple conflicting IDE configs found
176
+ */
177
+ class IdeDetectionError extends IdeError {
178
+ /**
179
+ * @param {string} ideName - Name of the IDE
180
+ * @param {string} reason - Why detection failed
181
+ * @param {Object} [context={}] - Additional context
182
+ */
183
+ constructor(ideName, reason, context = {}) {
184
+ super(`IDE detection failed: ${reason}`, ideName, {
185
+ reason,
186
+ ...context,
187
+ });
188
+ this.reason = reason;
189
+ }
190
+ }
191
+
192
+ /**
193
+ * Wrap a function call and convert EACCES/EPERM errors to FilePermissionError
194
+ * @param {string} ideName - Name of the IDE
195
+ * @param {string} filePath - Path being accessed
196
+ * @param {string} operation - Operation type ('read', 'write', 'delete')
197
+ * @param {Function} fn - Async function to wrap
198
+ * @returns {Promise<any>} Result of the function
199
+ * @throws {FilePermissionError} If permission error occurs
200
+ */
201
+ async function withPermissionHandling(ideName, filePath, operation, fn) {
202
+ try {
203
+ return await fn();
204
+ } catch (error) {
205
+ if (error.code === 'EACCES' || error.code === 'EPERM') {
206
+ throw new FilePermissionError(ideName, filePath, operation, {
207
+ originalError: error.message,
208
+ });
209
+ }
210
+ throw error;
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Check if an error is an IDE-related error
216
+ * @param {Error} error - Error to check
217
+ * @returns {boolean}
218
+ */
219
+ function isIdeError(error) {
220
+ return error instanceof IdeError;
221
+ }
222
+
223
+ module.exports = {
224
+ IdeError,
225
+ IdeConfigNotFoundError,
226
+ CommandInstallationError,
227
+ FilePermissionError,
228
+ ContentInjectionError,
229
+ CleanupError,
230
+ IdeDetectionError,
231
+ withPermissionHandling,
232
+ isIdeError,
233
+ };