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.
- package/CHANGELOG.md +5 -0
- package/lib/file-cache.js +358 -0
- package/lib/progress.js +331 -0
- package/lib/validate.js +281 -1
- package/lib/yaml-utils.js +122 -0
- package/package.json +1 -1
- package/scripts/agileflow-welcome.js +19 -33
- package/scripts/obtain-context.js +4 -5
- package/tools/cli/installers/core/installer.js +32 -2
- package/tools/cli/installers/ide/_base-ide.js +143 -19
- package/tools/cli/installers/ide/claude-code.js +14 -51
- package/tools/cli/installers/ide/cursor.js +4 -40
- package/tools/cli/installers/ide/windsurf.js +6 -40
- package/tools/cli/lib/content-injector.js +37 -0
- package/tools/cli/lib/ide-errors.js +233 -0
|
@@ -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
|
+
};
|