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,363 @@
1
+ /**
2
+ * result-schema.js - Unified Result<T> type for consistent return values
3
+ *
4
+ * Provides standardized Result type with metadata for:
5
+ * - Consistent success/failure handling
6
+ * - Error code integration
7
+ * - Severity and category tracking
8
+ * - Automatic recovery suggestions
9
+ *
10
+ * Usage:
11
+ * const { success, failure, createResult, isSuccess } = require('./result-schema');
12
+ *
13
+ * // Success case
14
+ * return success(data);
15
+ *
16
+ * // Failure case
17
+ * return failure('ENOENT', 'File not found');
18
+ *
19
+ * // Check result
20
+ * if (isSuccess(result)) {
21
+ * console.log(result.data);
22
+ * } else {
23
+ * console.error(result.error);
24
+ * }
25
+ */
26
+
27
+ const { ErrorCodes, Severity, Category, getErrorCode } = require('./error-codes');
28
+
29
+ /**
30
+ * @typedef {Object} ResultSuccess<T>
31
+ * @property {true} ok - Indicates success
32
+ * @property {T} data - The success data
33
+ * @property {undefined} error - No error on success
34
+ * @property {undefined} errorCode - No error code on success
35
+ */
36
+
37
+ /**
38
+ * @typedef {Object} ResultFailure
39
+ * @property {false} ok - Indicates failure
40
+ * @property {undefined} data - No data on failure
41
+ * @property {string} error - Human-readable error message
42
+ * @property {string} errorCode - Machine-readable error code
43
+ * @property {string} severity - Error severity (critical, high, medium, low)
44
+ * @property {string} category - Error category (filesystem, permission, etc.)
45
+ * @property {boolean} recoverable - Whether the error can be recovered from
46
+ * @property {string} [suggestedFix] - Suggested fix for the error
47
+ * @property {string} [autoFix] - Auto-fix action name if available
48
+ * @property {Object} [context] - Additional context about the error
49
+ */
50
+
51
+ /**
52
+ * @typedef {ResultSuccess<T> | ResultFailure} Result<T>
53
+ */
54
+
55
+ /**
56
+ * Create a success result
57
+ * @template T
58
+ * @param {T} data - The success data
59
+ * @param {Object} [meta] - Optional metadata
60
+ * @returns {ResultSuccess<T>}
61
+ */
62
+ function success(data, meta = {}) {
63
+ return {
64
+ ok: true,
65
+ data,
66
+ ...meta,
67
+ };
68
+ }
69
+
70
+ /**
71
+ * Create a failure result with error code metadata
72
+ * @param {string} errorCode - Error code (e.g., 'ENOENT', 'ECONFIG')
73
+ * @param {string} [message] - Optional custom error message
74
+ * @param {Object} [options] - Additional options
75
+ * @param {Object} [options.context] - Additional context about the error
76
+ * @param {Error} [options.cause] - Original error that caused this failure
77
+ * @returns {ResultFailure}
78
+ */
79
+ function failure(errorCode, message, options = {}) {
80
+ const codeData = getErrorCode(errorCode) || ErrorCodes.EUNKNOWN;
81
+
82
+ return {
83
+ ok: false,
84
+ error: message || codeData.message,
85
+ errorCode: codeData.code,
86
+ severity: codeData.severity,
87
+ category: codeData.category,
88
+ recoverable: codeData.recoverable,
89
+ suggestedFix: codeData.suggestedFix,
90
+ autoFix: codeData.autoFix || null,
91
+ context: options.context,
92
+ cause: options.cause,
93
+ };
94
+ }
95
+
96
+ /**
97
+ * Create a failure result from an existing Error object
98
+ * @param {Error} error - The error object
99
+ * @param {string} [defaultCode='EUNKNOWN'] - Default error code if none detected
100
+ * @param {Object} [options] - Additional options
101
+ * @param {Object} [options.context] - Additional context about the error
102
+ * @returns {ResultFailure}
103
+ */
104
+ function failureFromError(error, defaultCode = 'EUNKNOWN', options = {}) {
105
+ const { getErrorCodeFromError } = require('./error-codes');
106
+ const codeData = getErrorCodeFromError(error);
107
+
108
+ // Use detected code or default
109
+ const code = codeData.code !== 'EUNKNOWN' ? codeData.code : defaultCode;
110
+ const finalCodeData = ErrorCodes[code] || ErrorCodes.EUNKNOWN;
111
+
112
+ return {
113
+ ok: false,
114
+ error: error.message || finalCodeData.message,
115
+ errorCode: finalCodeData.code,
116
+ severity: finalCodeData.severity,
117
+ category: finalCodeData.category,
118
+ recoverable: finalCodeData.recoverable,
119
+ suggestedFix: finalCodeData.suggestedFix,
120
+ autoFix: finalCodeData.autoFix || null,
121
+ context: options.context,
122
+ cause: error,
123
+ };
124
+ }
125
+
126
+ /**
127
+ * Create a result from a boolean condition
128
+ * @template T
129
+ * @param {boolean} condition - Condition to check
130
+ * @param {T} data - Data to return on success
131
+ * @param {string} errorCode - Error code on failure
132
+ * @param {string} [errorMessage] - Error message on failure
133
+ * @returns {Result<T>}
134
+ */
135
+ function fromCondition(condition, data, errorCode, errorMessage) {
136
+ if (condition) {
137
+ return success(data);
138
+ }
139
+ return failure(errorCode, errorMessage);
140
+ }
141
+
142
+ /**
143
+ * Create a result from a Promise
144
+ * @template T
145
+ * @param {Promise<T>} promise - Promise to wrap
146
+ * @param {string} [defaultCode='EUNKNOWN'] - Default error code on rejection
147
+ * @returns {Promise<Result<T>>}
148
+ */
149
+ async function fromPromise(promise, defaultCode = 'EUNKNOWN') {
150
+ try {
151
+ const data = await promise;
152
+ return success(data);
153
+ } catch (error) {
154
+ return failureFromError(error, defaultCode);
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Create a result from a sync function call
160
+ * @template T
161
+ * @param {() => T} fn - Function to execute
162
+ * @param {string} [defaultCode='EUNKNOWN'] - Default error code on throw
163
+ * @returns {Result<T>}
164
+ */
165
+ function fromTry(fn, defaultCode = 'EUNKNOWN') {
166
+ try {
167
+ const data = fn();
168
+ return success(data);
169
+ } catch (error) {
170
+ return failureFromError(error, defaultCode);
171
+ }
172
+ }
173
+
174
+ /**
175
+ * Check if a result is successful
176
+ * @template T
177
+ * @param {Result<T>} result - Result to check
178
+ * @returns {boolean}
179
+ */
180
+ function isSuccess(result) {
181
+ return result != null && result.ok === true;
182
+ }
183
+
184
+ /**
185
+ * Check if a result is a failure
186
+ * @template T
187
+ * @param {Result<T>} result - Result to check
188
+ * @returns {boolean}
189
+ */
190
+ function isFailure(result) {
191
+ return result != null && result.ok === false;
192
+ }
193
+
194
+ /**
195
+ * Get data from result or throw if failure
196
+ * @template T
197
+ * @param {Result<T>} result - Result to unwrap
198
+ * @param {string} [context] - Context for error message
199
+ * @returns {T}
200
+ * @throws {Error} If result is a failure
201
+ */
202
+ function unwrap(result, context) {
203
+ if (isSuccess(result)) {
204
+ return result.data;
205
+ }
206
+
207
+ const prefix = context ? `${context}: ` : '';
208
+ const error = new Error(`${prefix}${result.error}`);
209
+ error.errorCode = result.errorCode;
210
+ error.severity = result.severity;
211
+ error.category = result.category;
212
+ error.recoverable = result.recoverable;
213
+ error.suggestedFix = result.suggestedFix;
214
+ throw error;
215
+ }
216
+
217
+ /**
218
+ * Get data from result or return default value on failure
219
+ * @template T
220
+ * @param {Result<T>} result - Result to unwrap
221
+ * @param {T} defaultValue - Default value on failure
222
+ * @returns {T}
223
+ */
224
+ function unwrapOr(result, defaultValue) {
225
+ if (isSuccess(result)) {
226
+ return result.data;
227
+ }
228
+ return defaultValue;
229
+ }
230
+
231
+ /**
232
+ * Transform successful result data
233
+ * @template T, U
234
+ * @param {Result<T>} result - Result to transform
235
+ * @param {(data: T) => U} fn - Transform function
236
+ * @returns {Result<U>}
237
+ */
238
+ function map(result, fn) {
239
+ if (isSuccess(result)) {
240
+ return success(fn(result.data));
241
+ }
242
+ return result;
243
+ }
244
+
245
+ /**
246
+ * Chain result-returning operations
247
+ * @template T, U
248
+ * @param {Result<T>} result - Result to chain from
249
+ * @param {(data: T) => Result<U>} fn - Function returning Result
250
+ * @returns {Result<U>}
251
+ */
252
+ function flatMap(result, fn) {
253
+ if (isSuccess(result)) {
254
+ return fn(result.data);
255
+ }
256
+ return result;
257
+ }
258
+
259
+ /**
260
+ * Combine multiple results into one
261
+ * @template T
262
+ * @param {Result<T>[]} results - Results to combine
263
+ * @returns {Result<T[]>} Combined result with array of data or first failure
264
+ */
265
+ function all(results) {
266
+ const data = [];
267
+
268
+ for (const result of results) {
269
+ if (isFailure(result)) {
270
+ return result;
271
+ }
272
+ data.push(result.data);
273
+ }
274
+
275
+ return success(data);
276
+ }
277
+
278
+ /**
279
+ * Get first successful result or last failure
280
+ * @template T
281
+ * @param {Result<T>[]} results - Results to check
282
+ * @returns {Result<T>} First success or last failure
283
+ */
284
+ function any(results) {
285
+ let lastFailure = null;
286
+
287
+ for (const result of results) {
288
+ if (isSuccess(result)) {
289
+ return result;
290
+ }
291
+ lastFailure = result;
292
+ }
293
+
294
+ return lastFailure || failure('EUNKNOWN', 'No results provided');
295
+ }
296
+
297
+ /**
298
+ * Format a result for logging/display
299
+ * @template T
300
+ * @param {Result<T>} result - Result to format
301
+ * @param {Object} [options] - Format options
302
+ * @param {boolean} [options.includeData=false] - Include data in output
303
+ * @param {boolean} [options.includeSuggestion=true] - Include suggested fix
304
+ * @returns {string}
305
+ */
306
+ function format(result, options = {}) {
307
+ const { includeData = false, includeSuggestion = true } = options;
308
+
309
+ if (isSuccess(result)) {
310
+ if (includeData) {
311
+ return `[OK] ${JSON.stringify(result.data)}`;
312
+ }
313
+ return '[OK]';
314
+ }
315
+
316
+ const lines = [`[${result.errorCode}] ${result.error}`];
317
+ lines.push(` Severity: ${result.severity} | Category: ${result.category}`);
318
+
319
+ if (includeSuggestion && result.suggestedFix) {
320
+ lines.push(` Fix: ${result.suggestedFix}`);
321
+ }
322
+
323
+ if (result.autoFix) {
324
+ lines.push(` Auto-fix available: npx agileflow doctor --fix`);
325
+ }
326
+
327
+ return lines.join('\n');
328
+ }
329
+
330
+ module.exports = {
331
+ // Constructors
332
+ success,
333
+ failure,
334
+ failureFromError,
335
+
336
+ // From helpers
337
+ fromCondition,
338
+ fromPromise,
339
+ fromTry,
340
+
341
+ // Type guards
342
+ isSuccess,
343
+ isFailure,
344
+
345
+ // Extractors
346
+ unwrap,
347
+ unwrapOr,
348
+
349
+ // Transformers
350
+ map,
351
+ flatMap,
352
+
353
+ // Combinators
354
+ all,
355
+ any,
356
+
357
+ // Utilities
358
+ format,
359
+
360
+ // Re-export enums for convenience
361
+ Severity,
362
+ Category,
363
+ };
package/lib/result.js ADDED
@@ -0,0 +1,210 @@
1
+ /**
2
+ * result.js - Unified Result Schema for AgileFlow
3
+ *
4
+ * Provides consistent result objects across modules with type helpers.
5
+ *
6
+ * Standard Result Schema:
7
+ * { ok: boolean, data?: any, error?: string|Error }
8
+ *
9
+ * Extended fields (context-specific):
10
+ * - found: boolean (for lookup operations)
11
+ * - applied: number (for batch operations)
12
+ * - cleaned: number (for cleanup operations)
13
+ * - path: string (for file operations)
14
+ * - status: string (for state operations)
15
+ *
16
+ * Usage:
17
+ * const { ok, err, Result } = require('./result');
18
+ *
19
+ * // Success
20
+ * return ok({ path: '/saved/file.json' });
21
+ *
22
+ * // Failure
23
+ * return err('File not found');
24
+ *
25
+ * // With data
26
+ * return ok({ data: parsedConfig });
27
+ *
28
+ * // Type checking
29
+ * if (Result.isOk(result)) { ... }
30
+ */
31
+
32
+ /**
33
+ * Create a success result
34
+ *
35
+ * @param {Object} [extras={}] - Additional fields to include
36
+ * @returns {{ ok: true } & Object} Success result
37
+ *
38
+ * @example
39
+ * ok() // { ok: true }
40
+ * ok({ data: config }) // { ok: true, data: config }
41
+ * ok({ path: '/file.json', created: true }) // { ok: true, path: '/file.json', created: true }
42
+ */
43
+ function ok(extras = {}) {
44
+ return { ok: true, ...extras };
45
+ }
46
+
47
+ /**
48
+ * Create a failure result
49
+ *
50
+ * @param {string|Error} error - Error message or Error object
51
+ * @param {Object} [extras={}] - Additional fields to include
52
+ * @returns {{ ok: false, error: string } & Object} Failure result
53
+ *
54
+ * @example
55
+ * err('Not found') // { ok: false, error: 'Not found' }
56
+ * err(new Error('Failed')) // { ok: false, error: 'Failed' }
57
+ * err('Invalid', { code: 'EINVAL' }) // { ok: false, error: 'Invalid', code: 'EINVAL' }
58
+ */
59
+ function err(error, extras = {}) {
60
+ const message = error instanceof Error ? error.message : String(error);
61
+ return { ok: false, error: message, ...extras };
62
+ }
63
+
64
+ /**
65
+ * Result utilities for type checking and manipulation
66
+ */
67
+ const Result = {
68
+ /**
69
+ * Check if result is success
70
+ * @param {Object} result - Result to check
71
+ * @returns {boolean}
72
+ */
73
+ isOk(result) {
74
+ return Boolean(result && result.ok === true);
75
+ },
76
+
77
+ /**
78
+ * Check if result is failure
79
+ * @param {Object} result - Result to check
80
+ * @returns {boolean}
81
+ */
82
+ isErr(result) {
83
+ return Boolean(result && result.ok === false);
84
+ },
85
+
86
+ /**
87
+ * Unwrap result data or throw on error
88
+ * @param {Object} result - Result to unwrap
89
+ * @param {string} [context] - Context for error message
90
+ * @returns {any} The data field or the result without ok field
91
+ * @throws {Error} If result is not ok
92
+ */
93
+ unwrap(result, context = '') {
94
+ if (!Result.isOk(result)) {
95
+ const prefix = context ? `${context}: ` : '';
96
+ throw new Error(`${prefix}${result.error || 'Unknown error'}`);
97
+ }
98
+ return result.data !== undefined ? result.data : result;
99
+ },
100
+
101
+ /**
102
+ * Unwrap result data or return default value
103
+ * @param {Object} result - Result to unwrap
104
+ * @param {any} defaultValue - Value to return on error
105
+ * @returns {any}
106
+ */
107
+ unwrapOr(result, defaultValue) {
108
+ if (!Result.isOk(result)) {
109
+ return defaultValue;
110
+ }
111
+ return result.data !== undefined ? result.data : result;
112
+ },
113
+
114
+ /**
115
+ * Map over successful result
116
+ * @param {Object} result - Result to map
117
+ * @param {Function} fn - Function to apply to data
118
+ * @returns {Object} Mapped result or original error
119
+ */
120
+ map(result, fn) {
121
+ if (!Result.isOk(result)) {
122
+ return result;
123
+ }
124
+ try {
125
+ const data = result.data !== undefined ? result.data : result;
126
+ const mapped = fn(data);
127
+ return ok({ data: mapped });
128
+ } catch (e) {
129
+ return err(e);
130
+ }
131
+ },
132
+
133
+ /**
134
+ * Convert legacy { success: true/false } to standard { ok: true/false }
135
+ * @param {Object} legacyResult - Legacy result object
136
+ * @returns {Object} Standardized result
137
+ */
138
+ fromLegacy(legacyResult) {
139
+ if (legacyResult.success !== undefined) {
140
+ const { success, ...rest } = legacyResult;
141
+ return { ok: success, ...rest };
142
+ }
143
+ return legacyResult;
144
+ },
145
+
146
+ /**
147
+ * Convert standard result to legacy format for backwards compatibility
148
+ * @param {Object} result - Standard result object
149
+ * @returns {Object} Legacy result with success field
150
+ */
151
+ toLegacy(result) {
152
+ if (result.ok !== undefined) {
153
+ const { ok: isOk, ...rest } = result;
154
+ return { success: isOk, ...rest };
155
+ }
156
+ return result;
157
+ },
158
+ };
159
+
160
+ /**
161
+ * Async result helpers
162
+ */
163
+ const AsyncResult = {
164
+ /**
165
+ * Wrap an async function to return Result
166
+ * @param {Function} fn - Async function to wrap
167
+ * @returns {Function} Wrapped function returning Result
168
+ *
169
+ * @example
170
+ * const safeRead = AsyncResult.wrap(fs.promises.readFile);
171
+ * const result = await safeRead('file.txt');
172
+ * if (Result.isOk(result)) { console.log(result.data); }
173
+ */
174
+ wrap(fn) {
175
+ return async (...args) => {
176
+ try {
177
+ const data = await fn(...args);
178
+ return ok({ data });
179
+ } catch (e) {
180
+ return err(e);
181
+ }
182
+ };
183
+ },
184
+
185
+ /**
186
+ * Execute multiple async operations and collect results
187
+ * @param {Array<Promise>} promises - Promises to execute
188
+ * @returns {Promise<Object>} Result with all settled results
189
+ */
190
+ async all(promises) {
191
+ try {
192
+ const results = await Promise.all(promises);
193
+ const allOk = results.every(r => Result.isOk(r));
194
+ if (allOk) {
195
+ return ok({ data: results });
196
+ }
197
+ const errors = results.filter(r => Result.isErr(r)).map(r => r.error);
198
+ return err(errors.join('; '), { partial: results });
199
+ } catch (e) {
200
+ return err(e);
201
+ }
202
+ },
203
+ };
204
+
205
+ module.exports = {
206
+ ok,
207
+ err,
208
+ Result,
209
+ AsyncResult,
210
+ };
@@ -155,6 +155,11 @@ class SessionRegistry extends EventEmitter {
155
155
  * @returns {Object} Registry data
156
156
  */
157
157
  loadSync() {
158
+ // Return cached data if within TTL (performance optimization)
159
+ if (this._cache && Date.now() - this._cacheTime < this.cacheTTL) {
160
+ return this._cache;
161
+ }
162
+
158
163
  this._ensureDir();
159
164
  const result = this._jsonFile.readSync();
160
165
 
@@ -167,6 +172,14 @@ class SessionRegistry extends EventEmitter {
167
172
  return this._createDefaultRegistry();
168
173
  }
169
174
 
175
+ /**
176
+ * Invalidate cache (call after external modifications)
177
+ */
178
+ invalidateCache() {
179
+ this._cache = null;
180
+ this._cacheTime = 0;
181
+ }
182
+
170
183
  /**
171
184
  * Save registry
172
185
  * @param {Object} registry - Registry data to save