agileflow 2.90.7 → 2.92.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.
Files changed (144) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +6 -6
  3. package/lib/README.md +178 -0
  4. package/lib/codebase-indexer.js +818 -0
  5. package/lib/colors.js +190 -12
  6. package/lib/consent.js +232 -0
  7. package/lib/correlation.js +277 -0
  8. package/lib/error-codes.js +46 -0
  9. package/lib/errors.js +48 -6
  10. package/lib/file-cache.js +182 -0
  11. package/lib/format-error.js +156 -0
  12. package/lib/path-resolver.js +155 -7
  13. package/lib/paths.js +212 -20
  14. package/lib/placeholder-registry.js +205 -0
  15. package/lib/registry-di.js +358 -0
  16. package/lib/result-schema.js +363 -0
  17. package/lib/result.js +210 -0
  18. package/lib/session-registry.js +13 -0
  19. package/lib/session-state-machine.js +465 -0
  20. package/lib/validate-commands.js +308 -0
  21. package/lib/validate-names.js +3 -3
  22. package/lib/validate.js +116 -52
  23. package/package.json +4 -1
  24. package/scripts/af +34 -0
  25. package/scripts/agent-loop.js +63 -9
  26. package/scripts/agileflow-configure.js +2 -2
  27. package/scripts/agileflow-welcome.js +435 -23
  28. package/scripts/archive-completed-stories.sh +57 -11
  29. package/scripts/claude-tmux.sh +102 -0
  30. package/scripts/damage-control-bash.js +3 -70
  31. package/scripts/damage-control-edit.js +3 -20
  32. package/scripts/damage-control-write.js +3 -20
  33. package/scripts/dependency-check.js +310 -0
  34. package/scripts/get-env.js +11 -4
  35. package/scripts/lib/configure-detect.js +23 -1
  36. package/scripts/lib/configure-features.js +43 -2
  37. package/scripts/lib/context-formatter.js +771 -0
  38. package/scripts/lib/context-loader.js +699 -0
  39. package/scripts/lib/damage-control-utils.js +107 -0
  40. package/scripts/lib/json-utils.sh +162 -0
  41. package/scripts/lib/state-migrator.js +353 -0
  42. package/scripts/lib/story-state-machine.js +437 -0
  43. package/scripts/obtain-context.js +118 -1048
  44. package/scripts/pre-push-check.sh +46 -0
  45. package/scripts/precompact-context.sh +36 -11
  46. package/scripts/query-codebase.js +538 -0
  47. package/scripts/ralph-loop.js +5 -5
  48. package/scripts/session-manager.js +220 -42
  49. package/scripts/spawn-parallel.js +651 -0
  50. package/scripts/tui/blessed/data/watcher.js +180 -0
  51. package/scripts/tui/blessed/index.js +244 -0
  52. package/scripts/tui/blessed/panels/output.js +101 -0
  53. package/scripts/tui/blessed/panels/sessions.js +150 -0
  54. package/scripts/tui/blessed/panels/trace.js +97 -0
  55. package/scripts/tui/blessed/ui/help.js +77 -0
  56. package/scripts/tui/blessed/ui/screen.js +52 -0
  57. package/scripts/tui/blessed/ui/statusbar.js +47 -0
  58. package/scripts/tui/blessed/ui/tabbar.js +99 -0
  59. package/scripts/tui/index.js +38 -30
  60. package/scripts/validators/README.md +143 -0
  61. package/scripts/validators/component-validator.js +239 -0
  62. package/scripts/validators/json-schema-validator.js +186 -0
  63. package/scripts/validators/markdown-validator.js +152 -0
  64. package/scripts/validators/migration-validator.js +129 -0
  65. package/scripts/validators/security-validator.js +380 -0
  66. package/scripts/validators/story-format-validator.js +197 -0
  67. package/scripts/validators/test-result-validator.js +114 -0
  68. package/scripts/validators/workflow-validator.js +247 -0
  69. package/src/core/agents/accessibility.md +6 -0
  70. package/src/core/agents/adr-writer.md +6 -0
  71. package/src/core/agents/analytics.md +6 -0
  72. package/src/core/agents/api.md +6 -0
  73. package/src/core/agents/ci.md +6 -0
  74. package/src/core/agents/codebase-query.md +261 -0
  75. package/src/core/agents/compliance.md +6 -0
  76. package/src/core/agents/configuration-damage-control.md +6 -0
  77. package/src/core/agents/configuration-visual-e2e.md +6 -0
  78. package/src/core/agents/database.md +10 -0
  79. package/src/core/agents/datamigration.md +6 -0
  80. package/src/core/agents/design.md +6 -0
  81. package/src/core/agents/devops.md +6 -0
  82. package/src/core/agents/documentation.md +6 -0
  83. package/src/core/agents/epic-planner.md +6 -0
  84. package/src/core/agents/integrations.md +6 -0
  85. package/src/core/agents/mentor.md +6 -0
  86. package/src/core/agents/mobile.md +6 -0
  87. package/src/core/agents/monitoring.md +6 -0
  88. package/src/core/agents/multi-expert.md +6 -0
  89. package/src/core/agents/performance.md +6 -0
  90. package/src/core/agents/product.md +6 -0
  91. package/src/core/agents/qa.md +6 -0
  92. package/src/core/agents/readme-updater.md +6 -0
  93. package/src/core/agents/refactor.md +6 -0
  94. package/src/core/agents/research.md +6 -0
  95. package/src/core/agents/security.md +6 -0
  96. package/src/core/agents/testing.md +10 -0
  97. package/src/core/agents/ui.md +6 -0
  98. package/src/core/commands/adr.md +114 -0
  99. package/src/core/commands/agent.md +120 -0
  100. package/src/core/commands/assign.md +145 -0
  101. package/src/core/commands/audit.md +401 -0
  102. package/src/core/commands/babysit.md +32 -5
  103. package/src/core/commands/board.md +1 -0
  104. package/src/core/commands/changelog.md +118 -0
  105. package/src/core/commands/configure.md +42 -6
  106. package/src/core/commands/diagnose.md +114 -0
  107. package/src/core/commands/epic.md +205 -1
  108. package/src/core/commands/handoff.md +128 -0
  109. package/src/core/commands/help.md +76 -0
  110. package/src/core/commands/metrics.md +1 -0
  111. package/src/core/commands/pr.md +96 -0
  112. package/src/core/commands/research/analyze.md +1 -0
  113. package/src/core/commands/research/ask.md +2 -0
  114. package/src/core/commands/research/import.md +1 -0
  115. package/src/core/commands/research/list.md +2 -0
  116. package/src/core/commands/research/synthesize.md +584 -0
  117. package/src/core/commands/research/view.md +2 -0
  118. package/src/core/commands/roadmap/analyze.md +400 -0
  119. package/src/core/commands/session/new.md +113 -6
  120. package/src/core/commands/session/spawn.md +197 -0
  121. package/src/core/commands/sprint.md +22 -0
  122. package/src/core/commands/status.md +200 -1
  123. package/src/core/commands/story/list.md +9 -9
  124. package/src/core/commands/story/view.md +1 -0
  125. package/src/core/commands/story.md +143 -4
  126. package/src/core/experts/codebase-query/expertise.yaml +190 -0
  127. package/src/core/experts/codebase-query/question.md +73 -0
  128. package/src/core/experts/codebase-query/self-improve.md +105 -0
  129. package/src/core/templates/agileflow-metadata.json +55 -2
  130. package/src/core/templates/plan-template.md +125 -0
  131. package/src/core/templates/story-lifecycle.md +213 -0
  132. package/src/core/templates/story-template.md +4 -0
  133. package/src/core/templates/tdd-test-template.js +241 -0
  134. package/tools/cli/commands/setup.js +86 -0
  135. package/tools/cli/installers/core/installer.js +94 -0
  136. package/tools/cli/installers/ide/_base-ide.js +20 -11
  137. package/tools/cli/installers/ide/codex.js +29 -47
  138. package/tools/cli/lib/config-manager.js +17 -2
  139. package/tools/cli/lib/content-transformer.js +271 -0
  140. package/tools/cli/lib/error-handler.js +14 -22
  141. package/tools/cli/lib/ide-error-factory.js +421 -0
  142. package/tools/cli/lib/ide-health-monitor.js +364 -0
  143. package/tools/cli/lib/ide-registry.js +114 -1
  144. package/tools/cli/lib/ui.js +14 -25
@@ -0,0 +1,421 @@
1
+ /**
2
+ * AgileFlow CLI - IDE Error Factory
3
+ *
4
+ * Abstract Factory pattern for IDE-specific error handling strategies.
5
+ * Centralizes recovery patterns across all IDE installers.
6
+ *
7
+ * Strategy Types:
8
+ * - RetryStrategy: Retry with exponential backoff
9
+ * - FallbackStrategy: Try alternative approach
10
+ * - AbortStrategy: Fail with helpful message
11
+ * - SilentStrategy: Swallow error and continue
12
+ */
13
+
14
+ /**
15
+ * Base error strategy interface
16
+ */
17
+ class ErrorStrategy {
18
+ /**
19
+ * @param {string} name - Strategy name
20
+ * @param {Object} options - Strategy options
21
+ */
22
+ constructor(name, options = {}) {
23
+ this.name = name;
24
+ this.options = options;
25
+ }
26
+
27
+ /**
28
+ * Handle an error
29
+ * @param {Error} error - The error to handle
30
+ * @param {Object} context - Execution context
31
+ * @returns {Promise<{ recovered: boolean, result: any, error: Error | null }>}
32
+ */
33
+ async handle(error, context = {}) {
34
+ throw new Error('Strategy.handle() must be implemented');
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Retry strategy with exponential backoff
40
+ */
41
+ class RetryStrategy extends ErrorStrategy {
42
+ constructor(options = {}) {
43
+ super('retry', options);
44
+ this.maxRetries = options.maxRetries || 3;
45
+ this.baseDelayMs = options.baseDelayMs || 1000;
46
+ this.maxDelayMs = options.maxDelayMs || 30000;
47
+ this.backoffMultiplier = options.backoffMultiplier || 2;
48
+ this.retryCondition = options.retryCondition || (() => true);
49
+ }
50
+
51
+ async handle(error, context = {}) {
52
+ const { operation, attempt = 0 } = context;
53
+
54
+ // Check if we should retry
55
+ if (attempt >= this.maxRetries) {
56
+ return { recovered: false, result: null, error };
57
+ }
58
+
59
+ if (!this.retryCondition(error, context)) {
60
+ return { recovered: false, result: null, error };
61
+ }
62
+
63
+ // Calculate delay with exponential backoff
64
+ const delay = Math.min(
65
+ this.baseDelayMs * Math.pow(this.backoffMultiplier, attempt),
66
+ this.maxDelayMs
67
+ );
68
+
69
+ // Wait before retry
70
+ await new Promise(resolve => setTimeout(resolve, delay));
71
+
72
+ // Retry the operation
73
+ if (typeof operation === 'function') {
74
+ try {
75
+ const result = await operation();
76
+ return { recovered: true, result, error: null };
77
+ } catch (retryError) {
78
+ // Recursively retry
79
+ return this.handle(retryError, { ...context, attempt: attempt + 1 });
80
+ }
81
+ }
82
+
83
+ return { recovered: false, result: null, error };
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Fallback strategy - try alternative approach
89
+ */
90
+ class FallbackStrategy extends ErrorStrategy {
91
+ constructor(options = {}) {
92
+ super('fallback', options);
93
+ this.fallbackFn = options.fallbackFn;
94
+ this.fallbackCondition = options.fallbackCondition || (() => true);
95
+ }
96
+
97
+ async handle(error, context = {}) {
98
+ if (!this.fallbackCondition(error, context)) {
99
+ return { recovered: false, result: null, error };
100
+ }
101
+
102
+ if (typeof this.fallbackFn !== 'function') {
103
+ return { recovered: false, result: null, error };
104
+ }
105
+
106
+ try {
107
+ const result = await this.fallbackFn(error, context);
108
+ return { recovered: true, result, error: null };
109
+ } catch (fallbackError) {
110
+ return {
111
+ recovered: false,
112
+ result: null,
113
+ error: new AggregateError([error, fallbackError], 'Fallback also failed'),
114
+ };
115
+ }
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Abort strategy - fail with helpful message
121
+ */
122
+ class AbortStrategy extends ErrorStrategy {
123
+ constructor(options = {}) {
124
+ super('abort', options);
125
+ this.messageFormatter = options.messageFormatter || (e => e.message);
126
+ this.exitCode = options.exitCode || 1;
127
+ }
128
+
129
+ async handle(error, context = {}) {
130
+ const message = this.messageFormatter(error, context);
131
+ const enhancedError = new Error(message);
132
+ enhancedError.originalError = error;
133
+ enhancedError.exitCode = this.exitCode;
134
+ enhancedError.context = context;
135
+
136
+ return { recovered: false, result: null, error: enhancedError };
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Silent strategy - swallow error and continue
142
+ */
143
+ class SilentStrategy extends ErrorStrategy {
144
+ constructor(options = {}) {
145
+ super('silent', options);
146
+ this.defaultValue = options.defaultValue;
147
+ this.logFn = options.logFn;
148
+ }
149
+
150
+ async handle(error, context = {}) {
151
+ if (typeof this.logFn === 'function') {
152
+ this.logFn(error, context);
153
+ }
154
+ return { recovered: true, result: this.defaultValue, error: null };
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Composite strategy - try multiple strategies in order
160
+ */
161
+ class CompositeStrategy extends ErrorStrategy {
162
+ constructor(strategies = [], options = {}) {
163
+ super('composite', options);
164
+ this.strategies = strategies;
165
+ }
166
+
167
+ async handle(error, context = {}) {
168
+ for (const strategy of this.strategies) {
169
+ const result = await strategy.handle(error, context);
170
+ if (result.recovered) {
171
+ return result;
172
+ }
173
+ }
174
+ return { recovered: false, result: null, error };
175
+ }
176
+ }
177
+
178
+ /**
179
+ * IDE-specific error categories
180
+ */
181
+ const ErrorCategories = {
182
+ NETWORK: 'network',
183
+ FILESYSTEM: 'filesystem',
184
+ PERMISSION: 'permission',
185
+ VALIDATION: 'validation',
186
+ TIMEOUT: 'timeout',
187
+ UNKNOWN: 'unknown',
188
+ };
189
+
190
+ /**
191
+ * Categorize an error
192
+ * @param {Error} error - Error to categorize
193
+ * @returns {string} Error category
194
+ */
195
+ function categorizeError(error) {
196
+ const message = (error.message || '').toLowerCase();
197
+ const code = error.code || '';
198
+
199
+ // Check timeout first (before network, since ETIMEDOUT could be either)
200
+ if (code === 'ETIMEDOUT' || message.includes('timeout') || message.includes('timed out')) {
201
+ return ErrorCategories.TIMEOUT;
202
+ }
203
+
204
+ if (
205
+ code === 'ENOTFOUND' ||
206
+ code === 'ECONNREFUSED' ||
207
+ message.includes('network') ||
208
+ message.includes('fetch')
209
+ ) {
210
+ return ErrorCategories.NETWORK;
211
+ }
212
+
213
+ if (
214
+ code === 'ENOENT' ||
215
+ code === 'EISDIR' ||
216
+ code === 'ENOTDIR' ||
217
+ message.includes('no such file') ||
218
+ message.includes('directory')
219
+ ) {
220
+ return ErrorCategories.FILESYSTEM;
221
+ }
222
+
223
+ if (code === 'EACCES' || code === 'EPERM' || message.includes('permission')) {
224
+ return ErrorCategories.PERMISSION;
225
+ }
226
+
227
+ if (message.includes('valid') || message.includes('invalid') || message.includes('expected')) {
228
+ return ErrorCategories.VALIDATION;
229
+ }
230
+
231
+ return ErrorCategories.UNKNOWN;
232
+ }
233
+
234
+ /**
235
+ * IDE Error Factory - produces IDE-specific error strategies
236
+ */
237
+ class IdeErrorFactory {
238
+ constructor(ideName, options = {}) {
239
+ this.ideName = ideName;
240
+ this.options = options;
241
+ this.strategies = new Map();
242
+
243
+ // Register default strategies
244
+ this.registerDefaults();
245
+ }
246
+
247
+ /**
248
+ * Register default strategies for common error categories
249
+ */
250
+ registerDefaults() {
251
+ // Network errors - retry with backoff
252
+ this.register(
253
+ ErrorCategories.NETWORK,
254
+ () =>
255
+ new CompositeStrategy([
256
+ new RetryStrategy({
257
+ maxRetries: 3,
258
+ baseDelayMs: 1000,
259
+ retryCondition: e => categorizeError(e) === ErrorCategories.NETWORK,
260
+ }),
261
+ new AbortStrategy({
262
+ messageFormatter: e =>
263
+ `Network error while installing ${this.ideName}: ${e.message}\n` +
264
+ 'Please check your internet connection and try again.',
265
+ }),
266
+ ])
267
+ );
268
+
269
+ // Filesystem errors - try with fallback
270
+ this.register(
271
+ ErrorCategories.FILESYSTEM,
272
+ () =>
273
+ new AbortStrategy({
274
+ messageFormatter: e =>
275
+ `File system error while installing ${this.ideName}: ${e.message}\n` +
276
+ 'Please check that the path exists and is writable.',
277
+ })
278
+ );
279
+
280
+ // Permission errors - abort with help
281
+ this.register(
282
+ ErrorCategories.PERMISSION,
283
+ () =>
284
+ new AbortStrategy({
285
+ messageFormatter: e =>
286
+ `Permission denied while installing ${this.ideName}: ${e.message}\n` +
287
+ 'Please check file permissions or run with appropriate privileges.',
288
+ })
289
+ );
290
+
291
+ // Validation errors - abort
292
+ this.register(
293
+ ErrorCategories.VALIDATION,
294
+ () =>
295
+ new AbortStrategy({
296
+ messageFormatter: e => `Validation error while installing ${this.ideName}: ${e.message}`,
297
+ })
298
+ );
299
+
300
+ // Timeout errors - retry once
301
+ this.register(
302
+ ErrorCategories.TIMEOUT,
303
+ () =>
304
+ new CompositeStrategy([
305
+ new RetryStrategy({
306
+ maxRetries: 1,
307
+ baseDelayMs: 5000,
308
+ }),
309
+ new AbortStrategy({
310
+ messageFormatter: e =>
311
+ `Operation timed out while installing ${this.ideName}: ${e.message}\n` +
312
+ 'Please try again later.',
313
+ }),
314
+ ])
315
+ );
316
+
317
+ // Unknown errors - abort
318
+ this.register(
319
+ ErrorCategories.UNKNOWN,
320
+ () =>
321
+ new AbortStrategy({
322
+ messageFormatter: e => `Unexpected error while installing ${this.ideName}: ${e.message}`,
323
+ })
324
+ );
325
+ }
326
+
327
+ /**
328
+ * Register a strategy factory for an error category
329
+ * @param {string} category - Error category
330
+ * @param {Function} factory - Factory function returning a strategy
331
+ */
332
+ register(category, factory) {
333
+ this.strategies.set(category, factory);
334
+ }
335
+
336
+ /**
337
+ * Get strategy for an error
338
+ * @param {Error} error - Error to handle
339
+ * @returns {ErrorStrategy} Appropriate strategy
340
+ */
341
+ getStrategy(error) {
342
+ const category = categorizeError(error);
343
+ const factory = this.strategies.get(category) || this.strategies.get(ErrorCategories.UNKNOWN);
344
+ return factory ? factory() : new AbortStrategy();
345
+ }
346
+
347
+ /**
348
+ * Handle an error using the appropriate strategy
349
+ * @param {Error} error - Error to handle
350
+ * @param {Object} context - Execution context
351
+ * @returns {Promise<{ recovered: boolean, result: any, error: Error | null }>}
352
+ */
353
+ async handleError(error, context = {}) {
354
+ const strategy = this.getStrategy(error);
355
+ return strategy.handle(error, { ...context, ideName: this.ideName });
356
+ }
357
+
358
+ /**
359
+ * Create a wrapped function with error handling
360
+ * @param {Function} fn - Function to wrap
361
+ * @param {Object} context - Default context
362
+ * @returns {Function} Wrapped function
363
+ */
364
+ wrap(fn, context = {}) {
365
+ return async (...args) => {
366
+ try {
367
+ return await fn(...args);
368
+ } catch (error) {
369
+ const result = await this.handleError(error, { ...context, operation: () => fn(...args) });
370
+ if (result.recovered) {
371
+ return result.result;
372
+ }
373
+ throw result.error;
374
+ }
375
+ };
376
+ }
377
+ }
378
+
379
+ /**
380
+ * Factory registry for all IDEs
381
+ */
382
+ const factoryRegistry = new Map();
383
+
384
+ /**
385
+ * Get or create factory for an IDE
386
+ * @param {string} ideName - IDE identifier
387
+ * @param {Object} options - Factory options
388
+ * @returns {IdeErrorFactory}
389
+ */
390
+ function getFactory(ideName, options = {}) {
391
+ if (!factoryRegistry.has(ideName)) {
392
+ factoryRegistry.set(ideName, new IdeErrorFactory(ideName, options));
393
+ }
394
+ return factoryRegistry.get(ideName);
395
+ }
396
+
397
+ /**
398
+ * Reset factory registry (for testing)
399
+ */
400
+ function resetFactories() {
401
+ factoryRegistry.clear();
402
+ }
403
+
404
+ module.exports = {
405
+ // Strategy classes
406
+ ErrorStrategy,
407
+ RetryStrategy,
408
+ FallbackStrategy,
409
+ AbortStrategy,
410
+ SilentStrategy,
411
+ CompositeStrategy,
412
+
413
+ // Error categorization
414
+ ErrorCategories,
415
+ categorizeError,
416
+
417
+ // Factory
418
+ IdeErrorFactory,
419
+ getFactory,
420
+ resetFactories,
421
+ };