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.
- package/CHANGELOG.md +10 -0
- package/README.md +6 -6
- package/lib/README.md +178 -0
- package/lib/codebase-indexer.js +818 -0
- package/lib/colors.js +190 -12
- package/lib/consent.js +232 -0
- package/lib/correlation.js +277 -0
- package/lib/error-codes.js +46 -0
- package/lib/errors.js +48 -6
- package/lib/file-cache.js +182 -0
- package/lib/format-error.js +156 -0
- package/lib/path-resolver.js +155 -7
- package/lib/paths.js +212 -20
- package/lib/placeholder-registry.js +205 -0
- package/lib/registry-di.js +358 -0
- package/lib/result-schema.js +363 -0
- package/lib/result.js +210 -0
- package/lib/session-registry.js +13 -0
- package/lib/session-state-machine.js +465 -0
- package/lib/validate-commands.js +308 -0
- package/lib/validate-names.js +3 -3
- package/lib/validate.js +116 -52
- package/package.json +4 -1
- package/scripts/af +34 -0
- package/scripts/agent-loop.js +63 -9
- package/scripts/agileflow-configure.js +2 -2
- package/scripts/agileflow-welcome.js +435 -23
- package/scripts/archive-completed-stories.sh +57 -11
- package/scripts/claude-tmux.sh +102 -0
- package/scripts/damage-control-bash.js +3 -70
- package/scripts/damage-control-edit.js +3 -20
- package/scripts/damage-control-write.js +3 -20
- package/scripts/dependency-check.js +310 -0
- package/scripts/get-env.js +11 -4
- package/scripts/lib/configure-detect.js +23 -1
- package/scripts/lib/configure-features.js +43 -2
- package/scripts/lib/context-formatter.js +771 -0
- package/scripts/lib/context-loader.js +699 -0
- package/scripts/lib/damage-control-utils.js +107 -0
- package/scripts/lib/json-utils.sh +162 -0
- package/scripts/lib/state-migrator.js +353 -0
- package/scripts/lib/story-state-machine.js +437 -0
- package/scripts/obtain-context.js +118 -1048
- package/scripts/pre-push-check.sh +46 -0
- package/scripts/precompact-context.sh +36 -11
- package/scripts/query-codebase.js +538 -0
- package/scripts/ralph-loop.js +5 -5
- package/scripts/session-manager.js +220 -42
- package/scripts/spawn-parallel.js +651 -0
- package/scripts/tui/blessed/data/watcher.js +180 -0
- package/scripts/tui/blessed/index.js +244 -0
- package/scripts/tui/blessed/panels/output.js +101 -0
- package/scripts/tui/blessed/panels/sessions.js +150 -0
- package/scripts/tui/blessed/panels/trace.js +97 -0
- package/scripts/tui/blessed/ui/help.js +77 -0
- package/scripts/tui/blessed/ui/screen.js +52 -0
- package/scripts/tui/blessed/ui/statusbar.js +47 -0
- package/scripts/tui/blessed/ui/tabbar.js +99 -0
- package/scripts/tui/index.js +38 -30
- package/scripts/validators/README.md +143 -0
- package/scripts/validators/component-validator.js +239 -0
- package/scripts/validators/json-schema-validator.js +186 -0
- package/scripts/validators/markdown-validator.js +152 -0
- package/scripts/validators/migration-validator.js +129 -0
- package/scripts/validators/security-validator.js +380 -0
- package/scripts/validators/story-format-validator.js +197 -0
- package/scripts/validators/test-result-validator.js +114 -0
- package/scripts/validators/workflow-validator.js +247 -0
- package/src/core/agents/accessibility.md +6 -0
- package/src/core/agents/adr-writer.md +6 -0
- package/src/core/agents/analytics.md +6 -0
- package/src/core/agents/api.md +6 -0
- package/src/core/agents/ci.md +6 -0
- package/src/core/agents/codebase-query.md +261 -0
- package/src/core/agents/compliance.md +6 -0
- package/src/core/agents/configuration-damage-control.md +6 -0
- package/src/core/agents/configuration-visual-e2e.md +6 -0
- package/src/core/agents/database.md +10 -0
- package/src/core/agents/datamigration.md +6 -0
- package/src/core/agents/design.md +6 -0
- package/src/core/agents/devops.md +6 -0
- package/src/core/agents/documentation.md +6 -0
- package/src/core/agents/epic-planner.md +6 -0
- package/src/core/agents/integrations.md +6 -0
- package/src/core/agents/mentor.md +6 -0
- package/src/core/agents/mobile.md +6 -0
- package/src/core/agents/monitoring.md +6 -0
- package/src/core/agents/multi-expert.md +6 -0
- package/src/core/agents/performance.md +6 -0
- package/src/core/agents/product.md +6 -0
- package/src/core/agents/qa.md +6 -0
- package/src/core/agents/readme-updater.md +6 -0
- package/src/core/agents/refactor.md +6 -0
- package/src/core/agents/research.md +6 -0
- package/src/core/agents/security.md +6 -0
- package/src/core/agents/testing.md +10 -0
- package/src/core/agents/ui.md +6 -0
- package/src/core/commands/adr.md +114 -0
- package/src/core/commands/agent.md +120 -0
- package/src/core/commands/assign.md +145 -0
- package/src/core/commands/audit.md +401 -0
- package/src/core/commands/babysit.md +32 -5
- package/src/core/commands/board.md +1 -0
- package/src/core/commands/changelog.md +118 -0
- package/src/core/commands/configure.md +42 -6
- package/src/core/commands/diagnose.md +114 -0
- package/src/core/commands/epic.md +205 -1
- package/src/core/commands/handoff.md +128 -0
- package/src/core/commands/help.md +76 -0
- package/src/core/commands/metrics.md +1 -0
- package/src/core/commands/pr.md +96 -0
- package/src/core/commands/research/analyze.md +1 -0
- package/src/core/commands/research/ask.md +2 -0
- package/src/core/commands/research/import.md +1 -0
- package/src/core/commands/research/list.md +2 -0
- package/src/core/commands/research/synthesize.md +584 -0
- package/src/core/commands/research/view.md +2 -0
- package/src/core/commands/roadmap/analyze.md +400 -0
- package/src/core/commands/session/new.md +113 -6
- package/src/core/commands/session/spawn.md +197 -0
- package/src/core/commands/sprint.md +22 -0
- package/src/core/commands/status.md +200 -1
- package/src/core/commands/story/list.md +9 -9
- package/src/core/commands/story/view.md +1 -0
- package/src/core/commands/story.md +143 -4
- package/src/core/experts/codebase-query/expertise.yaml +190 -0
- package/src/core/experts/codebase-query/question.md +73 -0
- package/src/core/experts/codebase-query/self-improve.md +105 -0
- package/src/core/templates/agileflow-metadata.json +55 -2
- package/src/core/templates/plan-template.md +125 -0
- package/src/core/templates/story-lifecycle.md +213 -0
- package/src/core/templates/story-template.md +4 -0
- package/src/core/templates/tdd-test-template.js +241 -0
- package/tools/cli/commands/setup.js +86 -0
- package/tools/cli/installers/core/installer.js +94 -0
- package/tools/cli/installers/ide/_base-ide.js +20 -11
- package/tools/cli/installers/ide/codex.js +29 -47
- package/tools/cli/lib/config-manager.js +17 -2
- package/tools/cli/lib/content-transformer.js +271 -0
- package/tools/cli/lib/error-handler.js +14 -22
- package/tools/cli/lib/ide-error-factory.js +421 -0
- package/tools/cli/lib/ide-health-monitor.js +364 -0
- package/tools/cli/lib/ide-registry.js +114 -1
- 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
|
+
};
|
package/lib/session-registry.js
CHANGED
|
@@ -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
|