agileflow 2.85.0 → 2.86.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/README.md +3 -3
- package/lib/colors.js +23 -0
- package/package.json +1 -1
- package/scripts/agileflow-statusline.sh +31 -44
- package/scripts/agileflow-welcome.js +11 -8
- package/scripts/batch-pmap-loop.js +528 -0
- package/scripts/lib/colors.sh +106 -0
- package/scripts/lib/file-tracking.js +5 -3
- package/scripts/obtain-context.js +125 -14
- package/scripts/session-boundary.js +3 -3
- package/scripts/session-manager.js +303 -8
- package/scripts/test-session-boundary.js +80 -0
- package/src/core/agents/mentor.md +40 -2
- package/src/core/agents/orchestrator.md +35 -2
- package/src/core/commands/babysit.md +198 -674
- package/src/core/commands/batch.md +117 -2
- package/src/core/commands/metrics.md +62 -9
- package/src/core/commands/rpi.md +500 -0
- package/src/core/commands/session/new.md +30 -22
- package/src/core/commands/session/status.md +35 -2
- package/src/core/templates/session-state.json +32 -3
- package/tools/cli/commands/config.js +43 -21
- package/tools/cli/commands/doctor.js +8 -5
- package/tools/cli/commands/setup.js +14 -7
- package/tools/cli/commands/uninstall.js +8 -5
- package/tools/cli/commands/update.js +20 -10
- package/tools/cli/lib/content-injector.js +80 -0
- package/tools/cli/lib/error-handler.js +173 -0
- package/tools/cli/lib/ui.js +3 -2
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft-07/schema#",
|
|
3
3
|
"description": "AgileFlow session state tracking",
|
|
4
|
-
"schema_version":
|
|
4
|
+
"schema_version": 3,
|
|
5
5
|
"current_session": {
|
|
6
6
|
"id": "sess-20251206-100000",
|
|
7
7
|
"started_at": "2025-12-06T10:00:00Z",
|
|
8
8
|
"baseline_verified": false,
|
|
9
9
|
"initial_test_status": "not_run",
|
|
10
10
|
"current_story": null,
|
|
11
|
-
"active_agent": null
|
|
11
|
+
"active_agent": null,
|
|
12
|
+
"thread_type": "base",
|
|
13
|
+
"thread_complexity": "routine",
|
|
14
|
+
"expected_duration_minutes": null
|
|
12
15
|
},
|
|
16
|
+
"_thread_type_enum": ["base", "parallel", "chained", "fusion", "big", "long"],
|
|
17
|
+
"_thread_complexity_enum": ["trivial", "routine", "complex", "exploratory"],
|
|
13
18
|
"active_command": {
|
|
14
19
|
"_comment": "Tracks which command (babysit, etc.) is currently active for context preservation across compacts",
|
|
15
20
|
"name": null,
|
|
@@ -23,5 +28,29 @@
|
|
|
23
28
|
"final_test_status": "not_run",
|
|
24
29
|
"commits": []
|
|
25
30
|
},
|
|
26
|
-
"session_history": []
|
|
31
|
+
"session_history": [],
|
|
32
|
+
"batch_loop": {
|
|
33
|
+
"_comment": "State for batch pmap loop mode - iterative processing with quality gates",
|
|
34
|
+
"enabled": false,
|
|
35
|
+
"batch_id": null,
|
|
36
|
+
"pattern": null,
|
|
37
|
+
"action": null,
|
|
38
|
+
"quality_gate": "tests",
|
|
39
|
+
"current_item": null,
|
|
40
|
+
"items": {},
|
|
41
|
+
"summary": {
|
|
42
|
+
"total": 0,
|
|
43
|
+
"completed": 0,
|
|
44
|
+
"in_progress": 0,
|
|
45
|
+
"pending": 0,
|
|
46
|
+
"failed": 0
|
|
47
|
+
},
|
|
48
|
+
"iteration": 0,
|
|
49
|
+
"max_iterations": 50,
|
|
50
|
+
"started_at": null,
|
|
51
|
+
"completed_at": null,
|
|
52
|
+
"stopped_reason": null,
|
|
53
|
+
"last_failure": null
|
|
54
|
+
},
|
|
55
|
+
"_batch_item_status_enum": ["pending", "in_progress", "completed", "failed", "skipped"]
|
|
27
56
|
}
|
|
@@ -11,6 +11,7 @@ const yaml = require('js-yaml');
|
|
|
11
11
|
const { Installer } = require('../installers/core/installer');
|
|
12
12
|
const { IdeManager } = require('../installers/ide/manager');
|
|
13
13
|
const { displayLogo, displaySection, success, warning, error, info } = require('../lib/ui');
|
|
14
|
+
const { ErrorHandler } = require('../lib/error-handler');
|
|
14
15
|
|
|
15
16
|
const installer = new Installer();
|
|
16
17
|
const ideManager = new IdeManager();
|
|
@@ -33,9 +34,12 @@ module.exports = {
|
|
|
33
34
|
|
|
34
35
|
if (!status.installed) {
|
|
35
36
|
displayLogo();
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
const handler = new ErrorHandler('config');
|
|
38
|
+
handler.warning(
|
|
39
|
+
'No AgileFlow installation found',
|
|
40
|
+
'Initialize AgileFlow first',
|
|
41
|
+
'npx agileflow setup'
|
|
42
|
+
);
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
const manifestPath = path.join(status.path, '_cfg', 'manifest.yaml');
|
|
@@ -76,11 +80,13 @@ module.exports = {
|
|
|
76
80
|
|
|
77
81
|
process.exit(0);
|
|
78
82
|
} catch (err) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
const handler = new ErrorHandler('config');
|
|
84
|
+
handler.critical(
|
|
85
|
+
'Configuration operation failed',
|
|
86
|
+
'Check manifest file integrity',
|
|
87
|
+
'npx agileflow doctor --fix',
|
|
88
|
+
err
|
|
89
|
+
);
|
|
84
90
|
}
|
|
85
91
|
},
|
|
86
92
|
};
|
|
@@ -121,17 +127,24 @@ async function handleList(status) {
|
|
|
121
127
|
* Handle get subcommand
|
|
122
128
|
*/
|
|
123
129
|
async function handleGet(status, key) {
|
|
130
|
+
const handler = new ErrorHandler('config');
|
|
131
|
+
|
|
124
132
|
if (!key) {
|
|
125
|
-
|
|
126
|
-
|
|
133
|
+
handler.warning(
|
|
134
|
+
'Missing key',
|
|
135
|
+
'Provide a config key to get',
|
|
136
|
+
'npx agileflow config get <key>'
|
|
137
|
+
);
|
|
127
138
|
}
|
|
128
139
|
|
|
129
140
|
const validKeys = ['userName', 'ides', 'agileflowFolder', 'docsFolder', 'version'];
|
|
130
141
|
|
|
131
142
|
if (!validKeys.includes(key)) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
143
|
+
handler.warning(
|
|
144
|
+
`Invalid key: ${key}`,
|
|
145
|
+
`Valid keys: ${validKeys.join(', ')}`,
|
|
146
|
+
'npx agileflow config list'
|
|
147
|
+
);
|
|
135
148
|
}
|
|
136
149
|
|
|
137
150
|
let value;
|
|
@@ -160,17 +173,24 @@ async function handleGet(status, key) {
|
|
|
160
173
|
* Handle set subcommand
|
|
161
174
|
*/
|
|
162
175
|
async function handleSet(directory, status, manifestPath, key, value) {
|
|
176
|
+
const handler = new ErrorHandler('config');
|
|
177
|
+
|
|
163
178
|
if (!key || value === undefined) {
|
|
164
|
-
|
|
165
|
-
|
|
179
|
+
handler.warning(
|
|
180
|
+
'Missing arguments',
|
|
181
|
+
'Provide both key and value',
|
|
182
|
+
'npx agileflow config set <key> <value>'
|
|
183
|
+
);
|
|
166
184
|
}
|
|
167
185
|
|
|
168
186
|
const validKeys = ['userName', 'ides', 'agileflowFolder', 'docsFolder'];
|
|
169
187
|
|
|
170
188
|
if (!validKeys.includes(key)) {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
189
|
+
handler.warning(
|
|
190
|
+
`Invalid key: ${key}`,
|
|
191
|
+
`Valid keys: ${validKeys.join(', ')}`,
|
|
192
|
+
'npx agileflow config list'
|
|
193
|
+
);
|
|
174
194
|
}
|
|
175
195
|
|
|
176
196
|
displayLogo();
|
|
@@ -198,9 +218,11 @@ async function handleSet(directory, status, manifestPath, key, value) {
|
|
|
198
218
|
// Validate IDEs
|
|
199
219
|
for (const ide of newIdes) {
|
|
200
220
|
if (!validIdes.includes(ide)) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
221
|
+
handler.warning(
|
|
222
|
+
`Invalid IDE: ${ide}`,
|
|
223
|
+
`Valid IDEs: ${validIdes.join(', ')}`,
|
|
224
|
+
'npx agileflow config set ides "claude-code,cursor"'
|
|
225
|
+
);
|
|
204
226
|
}
|
|
205
227
|
}
|
|
206
228
|
|
|
@@ -20,6 +20,7 @@ const {
|
|
|
20
20
|
} = require('../lib/ui');
|
|
21
21
|
const { IdeManager } = require('../installers/ide/manager');
|
|
22
22
|
const { getCurrentVersion } = require('../lib/version-checker');
|
|
23
|
+
const { ErrorHandler } = require('../lib/error-handler');
|
|
23
24
|
|
|
24
25
|
const installer = new Installer();
|
|
25
26
|
|
|
@@ -306,11 +307,13 @@ module.exports = {
|
|
|
306
307
|
|
|
307
308
|
process.exit(issues > 0 ? 1 : 0);
|
|
308
309
|
} catch (err) {
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
310
|
+
const handler = new ErrorHandler('doctor');
|
|
311
|
+
handler.critical(
|
|
312
|
+
'Diagnostics failed',
|
|
313
|
+
'Check permissions and installation',
|
|
314
|
+
'npx agileflow setup',
|
|
315
|
+
err
|
|
316
|
+
);
|
|
314
317
|
}
|
|
315
318
|
},
|
|
316
319
|
};
|
|
@@ -22,6 +22,7 @@ const {
|
|
|
22
22
|
} = require('../lib/ui');
|
|
23
23
|
const { createDocsStructure } = require('../lib/docs-setup');
|
|
24
24
|
const { getLatestVersion } = require('../lib/npm-utils');
|
|
25
|
+
const { ErrorHandler } = require('../lib/error-handler');
|
|
25
26
|
|
|
26
27
|
const installer = new Installer();
|
|
27
28
|
const ideManager = new IdeManager();
|
|
@@ -101,8 +102,12 @@ module.exports = {
|
|
|
101
102
|
const coreResult = await installer.install(config);
|
|
102
103
|
|
|
103
104
|
if (!coreResult.success) {
|
|
104
|
-
|
|
105
|
-
|
|
105
|
+
const handler = new ErrorHandler('setup');
|
|
106
|
+
handler.warning(
|
|
107
|
+
'Core setup failed',
|
|
108
|
+
'Check directory permissions',
|
|
109
|
+
'npx agileflow doctor'
|
|
110
|
+
);
|
|
106
111
|
}
|
|
107
112
|
|
|
108
113
|
success(`Installed ${coreResult.counts.agents} agents`);
|
|
@@ -144,11 +149,13 @@ module.exports = {
|
|
|
144
149
|
|
|
145
150
|
process.exit(0);
|
|
146
151
|
} catch (err) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
+
const handler = new ErrorHandler('setup');
|
|
153
|
+
handler.critical(
|
|
154
|
+
'Setup failed',
|
|
155
|
+
'Check directory exists and has write permissions',
|
|
156
|
+
'npx agileflow doctor',
|
|
157
|
+
err
|
|
158
|
+
);
|
|
152
159
|
}
|
|
153
160
|
},
|
|
154
161
|
};
|
|
@@ -10,6 +10,7 @@ const fs = require('fs-extra');
|
|
|
10
10
|
const { Installer } = require('../installers/core/installer');
|
|
11
11
|
const { IdeManager } = require('../installers/ide/manager');
|
|
12
12
|
const { displayLogo, displaySection, success, warning, error, confirm } = require('../lib/ui');
|
|
13
|
+
const { ErrorHandler } = require('../lib/error-handler');
|
|
13
14
|
|
|
14
15
|
const installer = new Installer();
|
|
15
16
|
const ideManager = new IdeManager();
|
|
@@ -138,11 +139,13 @@ module.exports = {
|
|
|
138
139
|
|
|
139
140
|
process.exit(0);
|
|
140
141
|
} catch (err) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
142
|
+
const handler = new ErrorHandler('uninstall');
|
|
143
|
+
handler.critical(
|
|
144
|
+
'Uninstall failed',
|
|
145
|
+
'Check file permissions',
|
|
146
|
+
'sudo npx agileflow uninstall --force',
|
|
147
|
+
err
|
|
148
|
+
);
|
|
146
149
|
}
|
|
147
150
|
},
|
|
148
151
|
};
|
|
@@ -22,6 +22,7 @@ const {
|
|
|
22
22
|
} = require('../lib/ui');
|
|
23
23
|
const { createDocsStructure, getDocsFolderName } = require('../lib/docs-setup');
|
|
24
24
|
const { getLatestVersion } = require('../lib/npm-utils');
|
|
25
|
+
const { ErrorHandler } = require('../lib/error-handler');
|
|
25
26
|
|
|
26
27
|
const installer = new Installer();
|
|
27
28
|
const ideManager = new IdeManager();
|
|
@@ -76,9 +77,12 @@ module.exports = {
|
|
|
76
77
|
const status = await installer.getStatus(directory);
|
|
77
78
|
|
|
78
79
|
if (!status.installed) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
const handler = new ErrorHandler('update');
|
|
81
|
+
handler.warning(
|
|
82
|
+
'No AgileFlow installation found',
|
|
83
|
+
'Initialize AgileFlow first',
|
|
84
|
+
'npx agileflow setup'
|
|
85
|
+
);
|
|
82
86
|
}
|
|
83
87
|
|
|
84
88
|
displaySection('Updating AgileFlow', `Current version: ${status.version}`);
|
|
@@ -179,8 +183,12 @@ module.exports = {
|
|
|
179
183
|
const coreResult = await installer.install(config, { force: options.force });
|
|
180
184
|
|
|
181
185
|
if (!coreResult.success) {
|
|
182
|
-
|
|
183
|
-
|
|
186
|
+
const handler = new ErrorHandler('update');
|
|
187
|
+
handler.warning(
|
|
188
|
+
'Update failed',
|
|
189
|
+
'Try running doctor to diagnose issues',
|
|
190
|
+
'npx agileflow doctor --fix'
|
|
191
|
+
);
|
|
184
192
|
}
|
|
185
193
|
|
|
186
194
|
success('Updated core content');
|
|
@@ -237,11 +245,13 @@ module.exports = {
|
|
|
237
245
|
|
|
238
246
|
process.exit(0);
|
|
239
247
|
} catch (err) {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
248
|
+
const handler = new ErrorHandler('update');
|
|
249
|
+
handler.critical(
|
|
250
|
+
'Update failed',
|
|
251
|
+
'Check network connection and disk space',
|
|
252
|
+
'npx agileflow doctor',
|
|
253
|
+
err
|
|
254
|
+
);
|
|
245
255
|
}
|
|
246
256
|
},
|
|
247
257
|
};
|
|
@@ -202,6 +202,80 @@ function injectContent(content, context = {}) {
|
|
|
202
202
|
return result;
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
+
// =============================================================================
|
|
206
|
+
// Section Processing Functions (Progressive Disclosure)
|
|
207
|
+
// =============================================================================
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Extract section names from content
|
|
211
|
+
* @param {string} content - Content with section markers
|
|
212
|
+
* @returns {string[]} Array of section names
|
|
213
|
+
*/
|
|
214
|
+
function extractSectionNames(content) {
|
|
215
|
+
const sectionPattern = /<!-- SECTION: (\w+[-\w]*) -->/g;
|
|
216
|
+
const sections = [];
|
|
217
|
+
let match;
|
|
218
|
+
while ((match = sectionPattern.exec(content)) !== null) {
|
|
219
|
+
sections.push(match[1]);
|
|
220
|
+
}
|
|
221
|
+
return sections;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Filter content to only include specified sections
|
|
226
|
+
* Sections are marked with: <!-- SECTION: name --> ... <!-- END_SECTION -->
|
|
227
|
+
*
|
|
228
|
+
* @param {string} content - Content with section markers
|
|
229
|
+
* @param {string[]} activeSections - Sections to include (empty = include all)
|
|
230
|
+
* @returns {string} Content with only active sections
|
|
231
|
+
*/
|
|
232
|
+
function filterSections(content, activeSections = []) {
|
|
233
|
+
// If no active sections specified, include all content
|
|
234
|
+
if (!activeSections || activeSections.length === 0) {
|
|
235
|
+
return content;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Pattern matches: <!-- SECTION: name --> content <!-- END_SECTION -->
|
|
239
|
+
const sectionPattern = /<!-- SECTION: (\w+[-\w]*) -->([\s\S]*?)<!-- END_SECTION -->/g;
|
|
240
|
+
|
|
241
|
+
return content.replace(sectionPattern, (match, sectionName, sectionContent) => {
|
|
242
|
+
if (activeSections.includes(sectionName)) {
|
|
243
|
+
// Keep the section content, remove the markers
|
|
244
|
+
return sectionContent;
|
|
245
|
+
}
|
|
246
|
+
// Remove the entire section
|
|
247
|
+
return '';
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Remove all section markers but keep content
|
|
253
|
+
* Used when no filtering is needed but markers should be cleaned
|
|
254
|
+
*
|
|
255
|
+
* @param {string} content - Content with section markers
|
|
256
|
+
* @returns {string} Content without section markers
|
|
257
|
+
*/
|
|
258
|
+
function stripSectionMarkers(content) {
|
|
259
|
+
// Remove section start markers
|
|
260
|
+
let result = content.replace(/<!-- SECTION: \w+[-\w]* -->\n?/g, '');
|
|
261
|
+
// Remove section end markers
|
|
262
|
+
result = result.replace(/<!-- END_SECTION -->\n?/g, '');
|
|
263
|
+
return result;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Check if content has section markers
|
|
268
|
+
* @param {string} content - Content to check
|
|
269
|
+
* @returns {boolean} True if content has sections
|
|
270
|
+
*/
|
|
271
|
+
function hasSections(content) {
|
|
272
|
+
return /<!-- SECTION: \w+[-\w]* -->/.test(content);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// =============================================================================
|
|
276
|
+
// Utility Functions
|
|
277
|
+
// =============================================================================
|
|
278
|
+
|
|
205
279
|
/**
|
|
206
280
|
* Check if content has any template variables
|
|
207
281
|
* @param {string} content - Content to check
|
|
@@ -263,4 +337,10 @@ module.exports = {
|
|
|
263
337
|
injectContent,
|
|
264
338
|
hasPlaceholders,
|
|
265
339
|
getPlaceholderDocs,
|
|
340
|
+
|
|
341
|
+
// Section processing (progressive disclosure)
|
|
342
|
+
extractSectionNames,
|
|
343
|
+
filterSections,
|
|
344
|
+
stripSectionMarkers,
|
|
345
|
+
hasSections,
|
|
266
346
|
};
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* error-handler.js - Centralized Error Handling with Actionable Guidance
|
|
3
|
+
*
|
|
4
|
+
* Provides three-tier error handling:
|
|
5
|
+
* - INFO: Non-blocking issues (does not exit)
|
|
6
|
+
* - WARNING: Issues that should be addressed (exit 1)
|
|
7
|
+
* - CRITICAL: Severe errors (exit 1 + stack trace if DEBUG=1)
|
|
8
|
+
*
|
|
9
|
+
* Error output format: "X <problem> | Action: <what to do> | Run: <command>"
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const { c } = require('../../../lib/colors');
|
|
13
|
+
|
|
14
|
+
class ErrorHandler {
|
|
15
|
+
/**
|
|
16
|
+
* Create an ErrorHandler instance
|
|
17
|
+
* @param {string} commandName - Name of the command using this handler
|
|
18
|
+
*/
|
|
19
|
+
constructor(commandName = 'agileflow') {
|
|
20
|
+
this.commandName = commandName;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Format error with actionable guidance
|
|
25
|
+
* Output: "X <problem> | Action: <what to do> | Run: <command>"
|
|
26
|
+
*
|
|
27
|
+
* @param {string} message - The error message describing the problem
|
|
28
|
+
* @param {string} [actionText] - What the user should do to fix it
|
|
29
|
+
* @param {string} [commandHint] - Command to run to fix it
|
|
30
|
+
* @returns {string} Formatted error string
|
|
31
|
+
*/
|
|
32
|
+
formatError(message, actionText, commandHint) {
|
|
33
|
+
let output = `${c.red}\u2716${c.reset} ${message}`;
|
|
34
|
+
if (actionText) {
|
|
35
|
+
output += ` ${c.dim}|${c.reset} ${c.cyan}Action:${c.reset} ${actionText}`;
|
|
36
|
+
}
|
|
37
|
+
if (commandHint) {
|
|
38
|
+
output += ` ${c.dim}|${c.reset} ${c.green}Run:${c.reset} ${c.bold}${commandHint}${c.reset}`;
|
|
39
|
+
}
|
|
40
|
+
return output;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Format warning message (yellow indicator)
|
|
45
|
+
* @param {string} message - The warning message
|
|
46
|
+
* @param {string} [actionText] - What the user should do
|
|
47
|
+
* @param {string} [commandHint] - Command to run
|
|
48
|
+
* @returns {string} Formatted warning string
|
|
49
|
+
*/
|
|
50
|
+
formatWarning(message, actionText, commandHint) {
|
|
51
|
+
let output = `${c.yellow}\u26A0${c.reset} ${message}`;
|
|
52
|
+
if (actionText) {
|
|
53
|
+
output += ` ${c.dim}|${c.reset} ${c.cyan}Action:${c.reset} ${actionText}`;
|
|
54
|
+
}
|
|
55
|
+
if (commandHint) {
|
|
56
|
+
output += ` ${c.dim}|${c.reset} ${c.green}Run:${c.reset} ${c.bold}${commandHint}${c.reset}`;
|
|
57
|
+
}
|
|
58
|
+
return output;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* INFO: Non-blocking issue, continue execution (does NOT exit)
|
|
63
|
+
* Use for informational messages about issues that don't prevent operation.
|
|
64
|
+
*
|
|
65
|
+
* @param {string} message - The info message
|
|
66
|
+
* @param {string} [actionText] - Optional action hint
|
|
67
|
+
* @param {string} [commandHint] - Optional command hint
|
|
68
|
+
*/
|
|
69
|
+
info(message, actionText, commandHint) {
|
|
70
|
+
console.log(this.formatWarning(message, actionText, commandHint));
|
|
71
|
+
// Does NOT exit - allows continuation
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* WARNING: Issue that should be addressed (exit 1)
|
|
76
|
+
* Use for problems that prevent the operation from completing properly.
|
|
77
|
+
*
|
|
78
|
+
* @param {string} message - The warning message
|
|
79
|
+
* @param {string} [actionText] - What the user should do
|
|
80
|
+
* @param {string} [commandHint] - Command to run to fix it
|
|
81
|
+
*/
|
|
82
|
+
warning(message, actionText, commandHint) {
|
|
83
|
+
console.error(this.formatError(message, actionText, commandHint));
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* CRITICAL: Severe error (exit 1 + stack trace if DEBUG=1)
|
|
89
|
+
* Use for unexpected errors, crashes, or severe failures.
|
|
90
|
+
*
|
|
91
|
+
* @param {string} message - The error message
|
|
92
|
+
* @param {string} [actionText] - What the user should do
|
|
93
|
+
* @param {string} [commandHint] - Command to run
|
|
94
|
+
* @param {Error} [error] - Original error object for stack trace
|
|
95
|
+
*/
|
|
96
|
+
critical(message, actionText, commandHint, error) {
|
|
97
|
+
console.error(this.formatError(message, actionText, commandHint));
|
|
98
|
+
if (process.env.DEBUG === '1' && error?.stack) {
|
|
99
|
+
console.error(`\n${c.dim}Stack trace:${c.reset}`);
|
|
100
|
+
console.error(c.dim + error.stack + c.reset);
|
|
101
|
+
}
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Convenience method: Get formatted error string without exiting
|
|
107
|
+
* Useful for collecting multiple errors before displaying.
|
|
108
|
+
*
|
|
109
|
+
* @param {string} message - The error message
|
|
110
|
+
* @param {string} [actionText] - What the user should do
|
|
111
|
+
* @param {string} [commandHint] - Command to run
|
|
112
|
+
* @returns {string} Formatted error string
|
|
113
|
+
*/
|
|
114
|
+
errorWithAction(message, actionText, commandHint) {
|
|
115
|
+
return this.formatError(message, actionText, commandHint);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Convenience method: Get formatted warning string without exiting
|
|
120
|
+
*
|
|
121
|
+
* @param {string} message - The warning message
|
|
122
|
+
* @param {string} [actionText] - What the user should do
|
|
123
|
+
* @param {string} [commandHint] - Command to run
|
|
124
|
+
* @returns {string} Formatted warning string
|
|
125
|
+
*/
|
|
126
|
+
warningWithAction(message, actionText, commandHint) {
|
|
127
|
+
return this.formatWarning(message, actionText, commandHint);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Display multiple issues at once, then exit with appropriate code
|
|
132
|
+
* Useful for validation that collects all errors before reporting.
|
|
133
|
+
*
|
|
134
|
+
* @param {Array<{type: 'info'|'warning'|'error', message: string, action?: string, command?: string}>} issues
|
|
135
|
+
* @returns {boolean} True if there were errors/warnings (would normally exit)
|
|
136
|
+
*/
|
|
137
|
+
reportIssues(issues) {
|
|
138
|
+
let hasErrors = false;
|
|
139
|
+
let hasWarnings = false;
|
|
140
|
+
|
|
141
|
+
for (const issue of issues) {
|
|
142
|
+
if (issue.type === 'error') {
|
|
143
|
+
console.error(this.formatError(issue.message, issue.action, issue.command));
|
|
144
|
+
hasErrors = true;
|
|
145
|
+
} else if (issue.type === 'warning') {
|
|
146
|
+
console.error(this.formatWarning(issue.message, issue.action, issue.command));
|
|
147
|
+
hasWarnings = true;
|
|
148
|
+
} else {
|
|
149
|
+
console.log(this.formatWarning(issue.message, issue.action, issue.command));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (hasErrors || hasWarnings) {
|
|
154
|
+
process.exit(1);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return hasErrors || hasWarnings;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Create a pre-configured ErrorHandler instance
|
|
163
|
+
* @param {string} commandName - Name of the command
|
|
164
|
+
* @returns {ErrorHandler} New ErrorHandler instance
|
|
165
|
+
*/
|
|
166
|
+
function createErrorHandler(commandName) {
|
|
167
|
+
return new ErrorHandler(commandName);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
module.exports = {
|
|
171
|
+
ErrorHandler,
|
|
172
|
+
createErrorHandler,
|
|
173
|
+
};
|
package/tools/cli/lib/ui.js
CHANGED
|
@@ -9,6 +9,7 @@ const inquirer = require('inquirer');
|
|
|
9
9
|
const path = require('node:path');
|
|
10
10
|
const fs = require('node:fs');
|
|
11
11
|
const { IdeManager } = require('../installers/ide/manager');
|
|
12
|
+
const { BRAND_HEX } = require('../../../lib/colors');
|
|
12
13
|
|
|
13
14
|
// Load package.json for version
|
|
14
15
|
const packageJsonPath = path.join(__dirname, '..', '..', '..', 'package.json');
|
|
@@ -25,7 +26,7 @@ function displayLogo() {
|
|
|
25
26
|
██╔══██║██║ ██║██║██║ ██╔══╝ ██╔══╝ ██║ ██║ ██║██║███╗██║
|
|
26
27
|
██║ ██║╚██████╔╝██║███████╗███████╗██║ ███████╗╚██████╔╝╚███╔███╔╝
|
|
27
28
|
╚═╝ ╚═╝ ╚═════╝ ╚═╝╚══════╝╚══════╝╚═╝ ╚══════╝ ╚═════╝ ╚══╝╚══╝ `;
|
|
28
|
-
console.log(chalk.hex(
|
|
29
|
+
console.log(chalk.hex(BRAND_HEX)(logo));
|
|
29
30
|
console.log(chalk.dim(` AgileFlow v${packageJson.version} - AI-Driven Agile Development\n`));
|
|
30
31
|
}
|
|
31
32
|
|
|
@@ -35,7 +36,7 @@ function displayLogo() {
|
|
|
35
36
|
* @param {string} subtitle - Optional subtitle
|
|
36
37
|
*/
|
|
37
38
|
function displaySection(title, subtitle = null) {
|
|
38
|
-
console.log(chalk.bold.hex(
|
|
39
|
+
console.log(chalk.bold.hex(BRAND_HEX)(`\n${title}`));
|
|
39
40
|
if (subtitle) {
|
|
40
41
|
console.log(chalk.dim(subtitle));
|
|
41
42
|
}
|