edsger 0.10.3 → 0.12.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/dist/api/features/__tests__/feature-utils.test.js +38 -38
- package/dist/api/features/__tests__/status-updater.test.js +6 -6
- package/dist/api/features/batch-operations.d.ts +3 -2
- package/dist/api/features/feature-utils.d.ts +2 -2
- package/dist/api/features/feature-utils.js +6 -6
- package/dist/api/features/test-cases.d.ts +2 -2
- package/dist/api/features/user-stories.d.ts +2 -10
- package/dist/api/features/user-stories.js +0 -22
- package/dist/commands/workflow/core/feature-filter.js +1 -1
- package/dist/commands/workflow/core/workflow-logger.js +1 -1
- package/dist/commands/workflow/processor.d.ts +1 -1
- package/dist/commands/workflow/processor.js +3 -3
- package/dist/config/__tests__/feature-status.test.js +1 -1
- package/dist/config/feature-status.js +1 -1
- package/dist/phases/feature-analysis/context.d.ts +0 -2
- package/dist/phases/feature-analysis/context.js +1 -3
- package/dist/phases/feature-analysis/outcome.js +2 -2
- package/dist/phases/feature-analysis/prompts.js +29 -4
- package/dist/types/features.d.ts +4 -1
- package/dist/types/index.d.ts +5 -3
- package/package.json +1 -1
|
@@ -11,21 +11,21 @@ describe('Feature Utils', () => {
|
|
|
11
11
|
{
|
|
12
12
|
id: '1',
|
|
13
13
|
name: 'Feature 1',
|
|
14
|
-
status: '
|
|
14
|
+
status: 'ready_for_ai',
|
|
15
15
|
product_id: 'test-product',
|
|
16
16
|
updated_at: '2024-01-15T10:00:00Z',
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
id: '2',
|
|
20
20
|
name: 'Feature 2',
|
|
21
|
-
status: '
|
|
21
|
+
status: 'ready_for_ai',
|
|
22
22
|
product_id: 'test-product',
|
|
23
23
|
updated_at: '2024-01-10T10:00:00Z',
|
|
24
24
|
},
|
|
25
25
|
{
|
|
26
26
|
id: '3',
|
|
27
27
|
name: 'Feature 3',
|
|
28
|
-
status: '
|
|
28
|
+
status: 'ready_for_ai',
|
|
29
29
|
product_id: 'test-product',
|
|
30
30
|
updated_at: '2024-01-20T10:00:00Z',
|
|
31
31
|
},
|
|
@@ -41,21 +41,21 @@ describe('Feature Utils', () => {
|
|
|
41
41
|
{
|
|
42
42
|
id: '1',
|
|
43
43
|
name: 'Feature 1',
|
|
44
|
-
status: '
|
|
44
|
+
status: 'ready_for_ai',
|
|
45
45
|
product_id: 'test-product',
|
|
46
46
|
updated_at: '2024-01-15T10:00:00Z',
|
|
47
47
|
},
|
|
48
48
|
{
|
|
49
49
|
id: '2',
|
|
50
50
|
name: 'Feature 2',
|
|
51
|
-
status: '
|
|
51
|
+
status: 'ready_for_ai',
|
|
52
52
|
product_id: 'test-product',
|
|
53
53
|
// Missing updated_at
|
|
54
54
|
},
|
|
55
55
|
{
|
|
56
56
|
id: '3',
|
|
57
57
|
name: 'Feature 3',
|
|
58
|
-
status: '
|
|
58
|
+
status: 'ready_for_ai',
|
|
59
59
|
product_id: 'test-product',
|
|
60
60
|
updated_at: '2024-01-10T10:00:00Z',
|
|
61
61
|
},
|
|
@@ -77,7 +77,7 @@ describe('Feature Utils', () => {
|
|
|
77
77
|
{
|
|
78
78
|
id: '1',
|
|
79
79
|
name: 'Feature 1',
|
|
80
|
-
status: '
|
|
80
|
+
status: 'ready_for_ai',
|
|
81
81
|
product_id: 'test-product',
|
|
82
82
|
updated_at: '2024-01-15T10:00:00Z',
|
|
83
83
|
},
|
|
@@ -91,14 +91,14 @@ describe('Feature Utils', () => {
|
|
|
91
91
|
{
|
|
92
92
|
id: '1',
|
|
93
93
|
name: 'Feature 1',
|
|
94
|
-
status: '
|
|
94
|
+
status: 'ready_for_ai',
|
|
95
95
|
product_id: 'test-product',
|
|
96
96
|
updated_at: '2024-01-15T10:00:00Z',
|
|
97
97
|
},
|
|
98
98
|
{
|
|
99
99
|
id: '2',
|
|
100
100
|
name: 'Feature 2',
|
|
101
|
-
status: '
|
|
101
|
+
status: 'ready_for_ai',
|
|
102
102
|
product_id: 'test-product',
|
|
103
103
|
updated_at: '2024-01-10T10:00:00Z',
|
|
104
104
|
},
|
|
@@ -117,21 +117,21 @@ describe('Feature Utils', () => {
|
|
|
117
117
|
{
|
|
118
118
|
id: '1',
|
|
119
119
|
name: 'Feature 1',
|
|
120
|
-
status: '
|
|
120
|
+
status: 'ready_for_ai',
|
|
121
121
|
product_id: 'test-product',
|
|
122
122
|
updated_at: sameTimestamp,
|
|
123
123
|
},
|
|
124
124
|
{
|
|
125
125
|
id: '2',
|
|
126
126
|
name: 'Feature 2',
|
|
127
|
-
status: '
|
|
127
|
+
status: 'ready_for_ai',
|
|
128
128
|
product_id: 'test-product',
|
|
129
129
|
updated_at: sameTimestamp,
|
|
130
130
|
},
|
|
131
131
|
{
|
|
132
132
|
id: '3',
|
|
133
133
|
name: 'Feature 3',
|
|
134
|
-
status: '
|
|
134
|
+
status: 'ready_for_ai',
|
|
135
135
|
product_id: 'test-product',
|
|
136
136
|
updated_at: sameTimestamp,
|
|
137
137
|
},
|
|
@@ -146,21 +146,21 @@ describe('Feature Utils', () => {
|
|
|
146
146
|
{
|
|
147
147
|
id: '1',
|
|
148
148
|
name: 'Feature 1',
|
|
149
|
-
status: '
|
|
149
|
+
status: 'ready_for_ai',
|
|
150
150
|
product_id: 'test-product',
|
|
151
151
|
updated_at: '2024-01-15', // Date only
|
|
152
152
|
},
|
|
153
153
|
{
|
|
154
154
|
id: '2',
|
|
155
155
|
name: 'Feature 2',
|
|
156
|
-
status: '
|
|
156
|
+
status: 'ready_for_ai',
|
|
157
157
|
product_id: 'test-product',
|
|
158
158
|
updated_at: '2024-01-10T10:00:00.000Z', // Full ISO format
|
|
159
159
|
},
|
|
160
160
|
{
|
|
161
161
|
id: '3',
|
|
162
162
|
name: 'Feature 3',
|
|
163
|
-
status: '
|
|
163
|
+
status: 'ready_for_ai',
|
|
164
164
|
product_id: 'test-product',
|
|
165
165
|
updated_at: '2024-01-20T15:30:00Z', // ISO with time
|
|
166
166
|
},
|
|
@@ -178,7 +178,7 @@ describe('Feature Utils', () => {
|
|
|
178
178
|
{
|
|
179
179
|
id: '1',
|
|
180
180
|
name: 'Feature 1',
|
|
181
|
-
status: '
|
|
181
|
+
status: 'ready_for_ai',
|
|
182
182
|
product_id: 'test-product',
|
|
183
183
|
},
|
|
184
184
|
{
|
|
@@ -190,12 +190,12 @@ describe('Feature Utils', () => {
|
|
|
190
190
|
{
|
|
191
191
|
id: '3',
|
|
192
192
|
name: 'Feature 3',
|
|
193
|
-
status: '
|
|
193
|
+
status: 'ready_for_ai',
|
|
194
194
|
product_id: 'test-product',
|
|
195
195
|
},
|
|
196
196
|
];
|
|
197
|
-
const filtered = filterFeaturesByStatus(features, '
|
|
198
|
-
assert.strictEqual(filtered.length, 2, 'Should return 2
|
|
197
|
+
const filtered = filterFeaturesByStatus(features, 'ready_for_ai');
|
|
198
|
+
assert.strictEqual(filtered.length, 2, 'Should return 2 ready_for_ai features');
|
|
199
199
|
assert.strictEqual(filtered[0].id, '1', 'Should include Feature 1');
|
|
200
200
|
assert.strictEqual(filtered[1].id, '3', 'Should include Feature 3');
|
|
201
201
|
});
|
|
@@ -214,12 +214,12 @@ describe('Feature Utils', () => {
|
|
|
214
214
|
product_id: 'test-product',
|
|
215
215
|
},
|
|
216
216
|
];
|
|
217
|
-
const filtered = filterFeaturesByStatus(features, '
|
|
217
|
+
const filtered = filterFeaturesByStatus(features, 'ready_for_ai');
|
|
218
218
|
assert.strictEqual(filtered.length, 0, 'Should return empty array');
|
|
219
219
|
});
|
|
220
220
|
it('should handle empty input array', () => {
|
|
221
221
|
const features = [];
|
|
222
|
-
const filtered = filterFeaturesByStatus(features, '
|
|
222
|
+
const filtered = filterFeaturesByStatus(features, 'ready_for_ai');
|
|
223
223
|
assert.strictEqual(filtered.length, 0, 'Should return empty array');
|
|
224
224
|
});
|
|
225
225
|
});
|
|
@@ -230,21 +230,21 @@ describe('Feature Utils', () => {
|
|
|
230
230
|
{
|
|
231
231
|
id: 'urgent-feature',
|
|
232
232
|
name: 'Urgent Feature',
|
|
233
|
-
status: '
|
|
233
|
+
status: 'ready_for_ai',
|
|
234
234
|
product_id: 'test-product',
|
|
235
235
|
updated_at: '2024-01-01T08:00:00Z', // Very old - should be processed first
|
|
236
236
|
},
|
|
237
237
|
{
|
|
238
238
|
id: 'recent-feature',
|
|
239
239
|
name: 'Recent Feature',
|
|
240
|
-
status: '
|
|
240
|
+
status: 'ready_for_ai',
|
|
241
241
|
product_id: 'test-product',
|
|
242
242
|
updated_at: '2024-01-20T10:00:00Z', // Recent - should be processed last
|
|
243
243
|
},
|
|
244
244
|
{
|
|
245
245
|
id: 'medium-feature',
|
|
246
246
|
name: 'Medium Feature',
|
|
247
|
-
status: '
|
|
247
|
+
status: 'ready_for_ai',
|
|
248
248
|
product_id: 'test-product',
|
|
249
249
|
updated_at: '2024-01-10T12:00:00Z', // Medium age - should be processed second
|
|
250
250
|
},
|
|
@@ -265,21 +265,21 @@ describe('Feature Utils', () => {
|
|
|
265
265
|
{
|
|
266
266
|
id: '1',
|
|
267
267
|
name: 'Feature 1',
|
|
268
|
-
status: '
|
|
268
|
+
status: 'ready_for_ai',
|
|
269
269
|
product_id: 'test-product',
|
|
270
270
|
updated_at: '2024-01-15T10:00:00Z',
|
|
271
271
|
},
|
|
272
272
|
{
|
|
273
273
|
id: '2',
|
|
274
274
|
name: 'Feature 2',
|
|
275
|
-
status: '
|
|
275
|
+
status: 'ready_for_ai',
|
|
276
276
|
product_id: 'test-product',
|
|
277
277
|
updated_at: '2024-01-10T10:00:00Z',
|
|
278
278
|
},
|
|
279
279
|
{
|
|
280
280
|
id: '3',
|
|
281
281
|
name: 'Feature 3',
|
|
282
|
-
status: '
|
|
282
|
+
status: 'ready_for_ai',
|
|
283
283
|
product_id: 'test-product',
|
|
284
284
|
updated_at: '2024-01-20T10:00:00Z',
|
|
285
285
|
},
|
|
@@ -298,21 +298,21 @@ describe('Feature Utils', () => {
|
|
|
298
298
|
{
|
|
299
299
|
id: 'very-old-bug',
|
|
300
300
|
name: 'Critical Bug Fix',
|
|
301
|
-
status: '
|
|
301
|
+
status: 'ready_for_ai',
|
|
302
302
|
product_id: 'test-product',
|
|
303
303
|
updated_at: '2024-01-01T00:00:00Z', // Very old, should be highest priority
|
|
304
304
|
},
|
|
305
305
|
{
|
|
306
306
|
id: 'new-enhancement',
|
|
307
307
|
name: 'Nice to Have Feature',
|
|
308
|
-
status: '
|
|
308
|
+
status: 'ready_for_ai',
|
|
309
309
|
product_id: 'test-product',
|
|
310
310
|
updated_at: '2024-01-25T00:00:00Z', // Recent, should be lowest priority
|
|
311
311
|
},
|
|
312
312
|
{
|
|
313
313
|
id: 'medium-priority',
|
|
314
314
|
name: 'Important Feature',
|
|
315
|
-
status: '
|
|
315
|
+
status: 'ready_for_ai',
|
|
316
316
|
product_id: 'test-product',
|
|
317
317
|
updated_at: '2024-01-15T00:00:00Z', // Medium age, should be middle priority
|
|
318
318
|
},
|
|
@@ -328,36 +328,36 @@ describe('Feature Utils', () => {
|
|
|
328
328
|
assert.ok(ages[0] <= ages[1] && ages[1] <= ages[2], 'Features should be processed in age order (oldest first)');
|
|
329
329
|
});
|
|
330
330
|
});
|
|
331
|
-
describe('
|
|
331
|
+
describe('getReadyForAIFeatures integration behavior', () => {
|
|
332
332
|
it('should use sortFeaturesByUpdatedAt to ensure oldest-first processing', () => {
|
|
333
|
-
// This test verifies the integration between
|
|
334
|
-
// We can test this indirectly by testing the sorting function that
|
|
333
|
+
// This test verifies the integration between getReadyForAIFeatures and sortFeaturesByUpdatedAt
|
|
334
|
+
// We can test this indirectly by testing the sorting function that getReadyForAIFeatures uses
|
|
335
335
|
const mockApiResponse = [
|
|
336
336
|
{
|
|
337
337
|
id: 'feat-1',
|
|
338
338
|
name: 'Feature Added Last Week',
|
|
339
|
-
status: '
|
|
339
|
+
status: 'ready_for_ai',
|
|
340
340
|
product_id: 'product-123',
|
|
341
341
|
updated_at: '2024-01-18T10:00:00Z',
|
|
342
342
|
},
|
|
343
343
|
{
|
|
344
344
|
id: 'feat-2',
|
|
345
345
|
name: 'Feature Added Yesterday',
|
|
346
|
-
status: '
|
|
346
|
+
status: 'ready_for_ai',
|
|
347
347
|
product_id: 'product-123',
|
|
348
348
|
updated_at: '2024-01-24T15:30:00Z',
|
|
349
349
|
},
|
|
350
350
|
{
|
|
351
351
|
id: 'feat-3',
|
|
352
352
|
name: 'Feature Added Last Month',
|
|
353
|
-
status: '
|
|
353
|
+
status: 'ready_for_ai',
|
|
354
354
|
product_id: 'product-123',
|
|
355
355
|
updated_at: '2024-01-05T08:00:00Z',
|
|
356
356
|
},
|
|
357
357
|
];
|
|
358
|
-
// This simulates what
|
|
358
|
+
// This simulates what getReadyForAIFeatures does internally
|
|
359
359
|
const sortedForWorkflow = sortFeaturesByUpdatedAt(mockApiResponse);
|
|
360
|
-
// Verify oldest-first ordering that
|
|
360
|
+
// Verify oldest-first ordering that getReadyForAIFeatures should provide
|
|
361
361
|
assert.strictEqual(sortedForWorkflow[0].id, 'feat-3', 'Oldest feature should be first');
|
|
362
362
|
assert.strictEqual(sortedForWorkflow[1].id, 'feat-1', 'Second oldest should be second');
|
|
363
363
|
assert.strictEqual(sortedForWorkflow[2].id, 'feat-2', 'Newest feature should be last');
|
|
@@ -8,7 +8,7 @@ import { isForwardProgression } from '../status-updater.js';
|
|
|
8
8
|
describe('Status Progression Logic', () => {
|
|
9
9
|
describe('isForwardProgression', () => {
|
|
10
10
|
it('should allow forward progression', () => {
|
|
11
|
-
assert.strictEqual(isForwardProgression('backlog', '
|
|
11
|
+
assert.strictEqual(isForwardProgression('backlog', 'ready_for_ai'), true, 'Should allow progression from backlog to ready_for_ai');
|
|
12
12
|
assert.strictEqual(isForwardProgression('feature_analysis', 'technical_design'), true, 'Should allow progression from feature_analysis to technical_design');
|
|
13
13
|
assert.strictEqual(isForwardProgression('code_implementation', 'shipped'), true, 'Should allow progression from code_implementation to shipped');
|
|
14
14
|
});
|
|
@@ -18,7 +18,7 @@ describe('Status Progression Logic', () => {
|
|
|
18
18
|
assert.strictEqual(isForwardProgression('shipped', 'shipped'), true, 'Should allow staying at shipped status');
|
|
19
19
|
});
|
|
20
20
|
it('should prevent backward progression', () => {
|
|
21
|
-
assert.strictEqual(isForwardProgression('
|
|
21
|
+
assert.strictEqual(isForwardProgression('ready_for_ai', 'backlog'), false, 'Should prevent regression from ready_for_ai to backlog');
|
|
22
22
|
assert.strictEqual(isForwardProgression('technical_design', 'feature_analysis'), false, 'Should prevent regression from technical_design to feature_analysis');
|
|
23
23
|
assert.strictEqual(isForwardProgression('shipped', 'code_implementation'), false, 'Should prevent regression from shipped to code_implementation');
|
|
24
24
|
});
|
|
@@ -42,7 +42,7 @@ describe('Status Progression Logic', () => {
|
|
|
42
42
|
it('should contain all expected statuses', () => {
|
|
43
43
|
const expectedStatuses = [
|
|
44
44
|
'backlog',
|
|
45
|
-
'
|
|
45
|
+
'ready_for_ai',
|
|
46
46
|
'feature_analysis',
|
|
47
47
|
'feature_analysis_verification',
|
|
48
48
|
'technical_design',
|
|
@@ -68,13 +68,13 @@ describe('Status Progression Logic', () => {
|
|
|
68
68
|
});
|
|
69
69
|
it('should have correct ordering for key workflow phases', () => {
|
|
70
70
|
const backlogIndex = STATUS_PROGRESSION_ORDER.indexOf('backlog');
|
|
71
|
-
const readyForDevIndex = STATUS_PROGRESSION_ORDER.indexOf('
|
|
71
|
+
const readyForDevIndex = STATUS_PROGRESSION_ORDER.indexOf('ready_for_ai');
|
|
72
72
|
const featureAnalysisIndex = STATUS_PROGRESSION_ORDER.indexOf('feature_analysis');
|
|
73
73
|
const technicalDesignIndex = STATUS_PROGRESSION_ORDER.indexOf('technical_design');
|
|
74
74
|
const codeImplementationIndex = STATUS_PROGRESSION_ORDER.indexOf('code_implementation');
|
|
75
75
|
const shippedIndex = STATUS_PROGRESSION_ORDER.indexOf('shipped');
|
|
76
|
-
assert.ok(backlogIndex < readyForDevIndex, 'backlog should come before
|
|
77
|
-
assert.ok(readyForDevIndex < featureAnalysisIndex, '
|
|
76
|
+
assert.ok(backlogIndex < readyForDevIndex, 'backlog should come before ready_for_ai');
|
|
77
|
+
assert.ok(readyForDevIndex < featureAnalysisIndex, 'ready_for_ai should come before feature_analysis');
|
|
78
78
|
assert.ok(featureAnalysisIndex < technicalDesignIndex, 'feature_analysis should come before technical_design');
|
|
79
79
|
assert.ok(technicalDesignIndex < codeImplementationIndex, 'technical_design should come before code_implementation');
|
|
80
80
|
assert.ok(codeImplementationIndex < shippedIndex, 'code_implementation should come before shipped');
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
+
import { UserStoryStatus, TestCaseStatus } from '../../types/index.js';
|
|
1
2
|
/**
|
|
2
3
|
* Batch update user story statuses
|
|
3
4
|
*/
|
|
4
|
-
export declare function batchUpdateUserStoryStatus(userStoryIds: string[], status:
|
|
5
|
+
export declare function batchUpdateUserStoryStatus(userStoryIds: string[], status: UserStoryStatus, verbose?: boolean): Promise<boolean>;
|
|
5
6
|
/**
|
|
6
7
|
* Batch update test case statuses
|
|
7
8
|
*/
|
|
8
|
-
export declare function batchUpdateTestCaseStatus(testCaseIds: string[], status:
|
|
9
|
+
export declare function batchUpdateTestCaseStatus(testCaseIds: string[], status: TestCaseStatus, verbose?: boolean): Promise<boolean>;
|
|
9
10
|
/**
|
|
10
11
|
* Batch delete user stories
|
|
11
12
|
*/
|
|
@@ -8,6 +8,6 @@ export declare function filterFeaturesByStatus(features: FeatureInfo[], status:
|
|
|
8
8
|
*/
|
|
9
9
|
export declare function sortFeaturesByUpdatedAt(features: FeatureInfo[]): FeatureInfo[];
|
|
10
10
|
/**
|
|
11
|
-
* Get features with
|
|
11
|
+
* Get features with ready_for_ai status for a product
|
|
12
12
|
*/
|
|
13
|
-
export declare function
|
|
13
|
+
export declare function getReadyForAIFeatures(productId: string, verbose?: boolean): Promise<FeatureInfo[]>;
|
|
@@ -17,21 +17,21 @@ export function sortFeaturesByUpdatedAt(features) {
|
|
|
17
17
|
});
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
|
-
* Get features with
|
|
20
|
+
* Get features with ready_for_ai status for a product
|
|
21
21
|
*/
|
|
22
|
-
export async function
|
|
22
|
+
export async function getReadyForAIFeatures(productId, verbose) {
|
|
23
23
|
if (verbose) {
|
|
24
|
-
logInfo(`Fetching
|
|
24
|
+
logInfo(`Fetching ready_for_ai features for product: ${productId}`);
|
|
25
25
|
}
|
|
26
26
|
try {
|
|
27
27
|
const result = (await callMcpEndpoint('features/list', {
|
|
28
28
|
product_id: productId,
|
|
29
|
-
status: '
|
|
29
|
+
status: 'ready_for_ai',
|
|
30
30
|
}));
|
|
31
31
|
const features = result.features || [];
|
|
32
32
|
const sortedFeatures = sortFeaturesByUpdatedAt(features);
|
|
33
33
|
if (verbose) {
|
|
34
|
-
logInfo(`✅ Found ${sortedFeatures.length}
|
|
34
|
+
logInfo(`✅ Found ${sortedFeatures.length} ready_for_ai features (oldest first)`);
|
|
35
35
|
sortedFeatures.forEach((feature, index) => {
|
|
36
36
|
logInfo(` ${index + 1}. ${feature.name} (updated: ${feature.updated_at})`);
|
|
37
37
|
});
|
|
@@ -40,7 +40,7 @@ export async function getReadyForDevFeatures(productId, verbose) {
|
|
|
40
40
|
}
|
|
41
41
|
catch (error) {
|
|
42
42
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
43
|
-
logError(`Failed to fetch
|
|
43
|
+
logError(`Failed to fetch ready_for_ai features: ${errorMessage}`);
|
|
44
44
|
throw error;
|
|
45
45
|
}
|
|
46
46
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TestCase } from '../../types/features.js';
|
|
1
|
+
import { TestCase, TestCaseStatus } from '../../types/features.js';
|
|
2
2
|
/**
|
|
3
3
|
* Get test cases for a feature
|
|
4
4
|
*/
|
|
@@ -26,4 +26,4 @@ export declare function deleteTestCase(testCaseId: string, verbose?: boolean): P
|
|
|
26
26
|
/**
|
|
27
27
|
* Update test case status
|
|
28
28
|
*/
|
|
29
|
-
export declare function updateTestCaseStatus(testCaseId: string, status:
|
|
29
|
+
export declare function updateTestCaseStatus(testCaseId: string, status: TestCaseStatus, verbose?: boolean): Promise<boolean>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { UserStory } from '../../types/features.js';
|
|
1
|
+
import { UserStory, UserStoryStatus } from '../../types/features.js';
|
|
2
2
|
/**
|
|
3
3
|
* Get user stories for a feature
|
|
4
4
|
*/
|
|
@@ -11,14 +11,6 @@ export declare function createUserStory(featureId: string, userStory: {
|
|
|
11
11
|
description: string;
|
|
12
12
|
status?: string;
|
|
13
13
|
}, verbose?: boolean): Promise<boolean>;
|
|
14
|
-
/**
|
|
15
|
-
* Create multiple user stories for a feature
|
|
16
|
-
*/
|
|
17
|
-
export declare function createUserStories(mcpServerUrl: string, mcpToken: string, featureId: string, userStories: Array<{
|
|
18
|
-
title: string;
|
|
19
|
-
description: string;
|
|
20
|
-
status?: string;
|
|
21
|
-
}>, verbose?: boolean): Promise<boolean>;
|
|
22
14
|
/**
|
|
23
15
|
* Delete a user story
|
|
24
16
|
*/
|
|
@@ -26,4 +18,4 @@ export declare function deleteUserStory(userStoryId: string, verbose?: boolean):
|
|
|
26
18
|
/**
|
|
27
19
|
* Update user story status
|
|
28
20
|
*/
|
|
29
|
-
export declare function updateUserStoryStatus(userStoryId: string, status:
|
|
21
|
+
export declare function updateUserStoryStatus(userStoryId: string, status: UserStoryStatus, verbose?: boolean): Promise<boolean>;
|
|
@@ -41,28 +41,6 @@ export async function createUserStory(featureId, userStory, verbose) {
|
|
|
41
41
|
return false;
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
|
-
/**
|
|
45
|
-
* Create multiple user stories for a feature
|
|
46
|
-
*/
|
|
47
|
-
export async function createUserStories(mcpServerUrl, mcpToken, featureId, userStories, verbose) {
|
|
48
|
-
try {
|
|
49
|
-
if (verbose) {
|
|
50
|
-
logInfo(`Creating ${userStories.length} user stories for feature: ${featureId}`);
|
|
51
|
-
}
|
|
52
|
-
for (const story of userStories) {
|
|
53
|
-
await createUserStory(featureId, story, false);
|
|
54
|
-
}
|
|
55
|
-
if (verbose) {
|
|
56
|
-
logInfo('✅ All user stories created successfully');
|
|
57
|
-
}
|
|
58
|
-
return true;
|
|
59
|
-
}
|
|
60
|
-
catch (error) {
|
|
61
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
62
|
-
logError(`Failed to create user stories: ${errorMessage}`);
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
44
|
/**
|
|
67
45
|
* Delete a user story
|
|
68
46
|
*/
|
|
@@ -9,7 +9,7 @@ export const shouldProcessFeature = (maxRetries) => (feature, states) => {
|
|
|
9
9
|
if (!state)
|
|
10
10
|
return true;
|
|
11
11
|
// If feature was updated after last processing attempt, should reprocess
|
|
12
|
-
// This handles cases where user manually changes status back to
|
|
12
|
+
// This handles cases where user manually changes status back to ready_for_ai
|
|
13
13
|
if (feature.updated_at) {
|
|
14
14
|
const featureUpdatedTime = new Date(feature.updated_at).getTime();
|
|
15
15
|
const lastAttemptTime = state.lastAttempt.getTime();
|
|
@@ -49,7 +49,7 @@ export const logFeatureError = (featureName, error) => {
|
|
|
49
49
|
logError(`❌ Error processing feature ${featureName}: ${error instanceof Error ? error.message : String(error)}`);
|
|
50
50
|
};
|
|
51
51
|
export const logNoFeaturesFound = () => {
|
|
52
|
-
logInfo('🔍 No
|
|
52
|
+
logInfo('🔍 No ready_for_ai features found, continuing to monitor...');
|
|
53
53
|
};
|
|
54
54
|
export const logAllFeaturesProcessed = () => {
|
|
55
55
|
logInfo('🔄 All current features are processed or being processed, continuing to monitor...');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Main workflow processor for continuous feature development
|
|
3
|
-
* Monitors for
|
|
3
|
+
* Monitors for ready_for_ai features and processes them through the complete pipeline
|
|
4
4
|
* Uses functional programming principles
|
|
5
5
|
*/
|
|
6
6
|
import { EdsgerConfig } from '../../types/index.js';
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Main workflow processor for continuous feature development
|
|
3
|
-
* Monitors for
|
|
3
|
+
* Monitors for ready_for_ai features and processes them through the complete pipeline
|
|
4
4
|
* Uses functional programming principles
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import { getReadyForAIFeatures } from '../../api/features/index.js';
|
|
7
7
|
import { runFeatureWorkflow } from './feature-coordinator.js';
|
|
8
8
|
import { logInfo } from '../../utils/logger.js';
|
|
9
9
|
// Import core modules
|
|
@@ -77,7 +77,7 @@ export class WorkflowProcessor {
|
|
|
77
77
|
return;
|
|
78
78
|
}
|
|
79
79
|
// Fetch features using unified API
|
|
80
|
-
const features = await
|
|
80
|
+
const features = await getReadyForAIFeatures(this.options.productId, this.options.verbose);
|
|
81
81
|
if (features.length === 0) {
|
|
82
82
|
if (this.options.verbose) {
|
|
83
83
|
logNoFeaturesFound();
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { createUserStories, createTestCases } from '../../api/features/index.js';
|
|
2
1
|
import type { FeatureInfo, UserStory, TestCase } from '../../types/features.js';
|
|
3
2
|
import { type ProductInfo } from '../../api/products.js';
|
|
4
3
|
import { ChecklistPhaseContext } from '../../services/checklist.js';
|
|
@@ -12,7 +11,6 @@ export interface FeatureAnalysisContext {
|
|
|
12
11
|
* Fetch all feature analysis context information via MCP endpoints
|
|
13
12
|
*/
|
|
14
13
|
export declare function fetchFeatureAnalysisContext(featureId: string, verbose?: boolean): Promise<FeatureAnalysisContext>;
|
|
15
|
-
export { createUserStories, createTestCases };
|
|
16
14
|
/**
|
|
17
15
|
* Format the context into a readable string for Claude Code
|
|
18
16
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { logInfo, logError } from '../../utils/logger.js';
|
|
2
|
-
import { getFeature, getUserStories,
|
|
2
|
+
import { getFeature, getUserStories, getTestCases, } from '../../api/features/index.js';
|
|
3
3
|
import { getProduct } from '../../api/products.js';
|
|
4
4
|
import { formatChecklistsForContext, } from '../../services/checklist.js';
|
|
5
5
|
import { getFeedbacksForPhase, formatFeedbacksForContext, } from '../../services/feedbacks.js';
|
|
@@ -40,8 +40,6 @@ export async function fetchFeatureAnalysisContext(featureId, verbose) {
|
|
|
40
40
|
throw new Error(`Context fetch failed: ${errorMessage}`);
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
|
-
// Re-export the create functions for convenience
|
|
44
|
-
export { createUserStories, createTestCases };
|
|
45
43
|
/**
|
|
46
44
|
* Format the context into a readable string for Claude Code
|
|
47
45
|
*/
|
|
@@ -180,7 +180,7 @@ export function buildAnalysisResult(featureId, context, structuredAnalysisResult
|
|
|
180
180
|
featureInfo: context.feature,
|
|
181
181
|
existingUserStories: context.existing_user_stories.map((story) => ({
|
|
182
182
|
...story,
|
|
183
|
-
status: story.status,
|
|
183
|
+
status: (story.status === 'ready' ? 'ready' : 'draft'),
|
|
184
184
|
created_at: story.created_at || new Date().toISOString(),
|
|
185
185
|
updated_at: story.updated_at || new Date().toISOString(),
|
|
186
186
|
})),
|
|
@@ -193,7 +193,7 @@ export function buildAnalysisResult(featureId, context, structuredAnalysisResult
|
|
|
193
193
|
id: '',
|
|
194
194
|
title: story.title,
|
|
195
195
|
description: story.description,
|
|
196
|
-
status:
|
|
196
|
+
status: 'draft',
|
|
197
197
|
created_at: new Date().toISOString(),
|
|
198
198
|
updated_at: new Date().toISOString(),
|
|
199
199
|
})),
|
|
@@ -88,14 +88,16 @@ You MUST return ONLY a JSON object with your analysis results. Do NOT include an
|
|
|
88
88
|
{
|
|
89
89
|
"title": "User story title",
|
|
90
90
|
"description": "As a [user], I want [goal] so that [benefit]",
|
|
91
|
-
"status": "draft"
|
|
91
|
+
"status": "draft",
|
|
92
|
+
"differentiation": "REQUIRED: Explain how this differs from existing user stories. Reference specific existing story titles and explain what new scenario/user type/workflow this covers that is NOT already addressed."
|
|
92
93
|
}
|
|
93
94
|
],
|
|
94
95
|
"created_test_cases": [
|
|
95
96
|
{
|
|
96
97
|
"name": "Test case name",
|
|
97
98
|
"description": "Detailed test case description with steps and expected outcomes",
|
|
98
|
-
"is_critical": true
|
|
99
|
+
"is_critical": true,
|
|
100
|
+
"differentiation": "REQUIRED: Explain how this differs from existing test cases. Reference specific existing test case names and explain what new scenario/edge case this covers that is NOT already tested."
|
|
99
101
|
}
|
|
100
102
|
],
|
|
101
103
|
"deleted_user_story_ids": [
|
|
@@ -141,6 +143,14 @@ deletion_reasons: {"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d": "Duplicate of test ca
|
|
|
141
143
|
**EXAMPLE - WRONG (DO NOT DO THIS)**:
|
|
142
144
|
deleted_test_case_ids: ["Duplicate test case", "Similar test"] // WRONG - these are text, not UUIDs
|
|
143
145
|
|
|
146
|
+
**CRITICAL - Differentiation Requirement**:
|
|
147
|
+
- The "differentiation" field is MANDATORY for every new user story and test case
|
|
148
|
+
- You MUST explain WHY this item is NOT a duplicate of existing items
|
|
149
|
+
- Reference specific existing item titles/names when explaining the difference
|
|
150
|
+
- If there are no existing items, write: "First item for this feature - no existing items to compare"
|
|
151
|
+
- If you cannot clearly articulate the difference, DO NOT create the item - it is likely a duplicate
|
|
152
|
+
- Generic explanations like "covers different scenario" are NOT acceptable - be specific
|
|
153
|
+
|
|
144
154
|
**Quality Guidelines**:
|
|
145
155
|
- User stories should be clear, concise, and user-focused
|
|
146
156
|
- Test cases should be comprehensive and cover all scenarios
|
|
@@ -300,10 +310,20 @@ You MUST return ONLY the JSON object below. Do NOT include any explanatory text,
|
|
|
300
310
|
"status": "success",
|
|
301
311
|
"summary": "Improved analysis based on verification feedback",
|
|
302
312
|
"created_user_stories": [
|
|
303
|
-
|
|
313
|
+
{
|
|
314
|
+
"title": "User story title",
|
|
315
|
+
"description": "As a [user], I want [goal] so that [benefit]",
|
|
316
|
+
"status": "draft",
|
|
317
|
+
"differentiation": "REQUIRED: Explain how this differs from existing user stories"
|
|
318
|
+
}
|
|
304
319
|
],
|
|
305
320
|
"created_test_cases": [
|
|
306
|
-
|
|
321
|
+
{
|
|
322
|
+
"name": "Test case name",
|
|
323
|
+
"description": "Detailed test case description",
|
|
324
|
+
"is_critical": true,
|
|
325
|
+
"differentiation": "REQUIRED: Explain how this differs from existing test cases"
|
|
326
|
+
}
|
|
307
327
|
],
|
|
308
328
|
"deleted_user_story_ids": [
|
|
309
329
|
// UUIDs of user stories to delete
|
|
@@ -327,6 +347,11 @@ You MUST return ONLY the JSON object below. Do NOT include any explanatory text,
|
|
|
327
347
|
}
|
|
328
348
|
\`\`\`
|
|
329
349
|
|
|
350
|
+
**CRITICAL - Differentiation Requirement**:
|
|
351
|
+
- The "differentiation" field is MANDATORY for every user story and test case
|
|
352
|
+
- You MUST explain WHY each item is NOT a duplicate of existing items
|
|
353
|
+
- If you cannot clearly articulate the difference, DO NOT include the item
|
|
354
|
+
|
|
330
355
|
MANDATORY: You MUST include checklist_item_results for ALL checklist items in your response. Every checklist item ID must be addressed - either passed or with explanation in notes why it cannot be satisfied. Missing any checklist item will cause the pipeline to fail again.
|
|
331
356
|
|
|
332
357
|
IMPORTANT: Return ONLY the JSON above. Do not add any text explaining what you did or how you improved the analysis. The JSON should be the complete and only content of your response.`;
|
package/dist/types/features.d.ts
CHANGED
|
@@ -10,11 +10,13 @@ export interface FeatureInfo {
|
|
|
10
10
|
created_at?: string;
|
|
11
11
|
updated_at?: string;
|
|
12
12
|
}
|
|
13
|
+
export type UserStoryStatus = 'draft' | 'ready';
|
|
14
|
+
export type TestCaseStatus = 'draft' | 'ready';
|
|
13
15
|
export interface UserStory {
|
|
14
16
|
id: string;
|
|
15
17
|
title: string;
|
|
16
18
|
description: string;
|
|
17
|
-
status:
|
|
19
|
+
status: UserStoryStatus;
|
|
18
20
|
created_at?: string;
|
|
19
21
|
updated_at?: string;
|
|
20
22
|
[key: string]: any;
|
|
@@ -24,6 +26,7 @@ export interface TestCase {
|
|
|
24
26
|
name: string;
|
|
25
27
|
description: string;
|
|
26
28
|
is_critical: boolean;
|
|
29
|
+
status?: TestCaseStatus;
|
|
27
30
|
created_at?: string;
|
|
28
31
|
updated_at?: string;
|
|
29
32
|
[key: string]: any;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -97,11 +97,13 @@ export interface FeatureData {
|
|
|
97
97
|
};
|
|
98
98
|
};
|
|
99
99
|
}
|
|
100
|
+
export type UserStoryStatus = 'draft' | 'ready';
|
|
101
|
+
export type TestCaseStatus = 'draft' | 'ready';
|
|
100
102
|
export interface UserStory {
|
|
101
103
|
id: string;
|
|
102
104
|
title: string;
|
|
103
105
|
description: string;
|
|
104
|
-
status:
|
|
106
|
+
status: UserStoryStatus;
|
|
105
107
|
created_at: string;
|
|
106
108
|
updated_at: string;
|
|
107
109
|
}
|
|
@@ -110,7 +112,7 @@ export interface TestCase {
|
|
|
110
112
|
name: string;
|
|
111
113
|
description: string;
|
|
112
114
|
is_critical: boolean;
|
|
113
|
-
status?:
|
|
115
|
+
status?: TestCaseStatus;
|
|
114
116
|
created_at: string;
|
|
115
117
|
updated_at: string;
|
|
116
118
|
}
|
|
@@ -128,4 +130,4 @@ export interface FeatureAnalysisDisplayResult {
|
|
|
128
130
|
createdUserStories?: DisplayUserStory[];
|
|
129
131
|
createdTestCases?: DisplayTestCase[];
|
|
130
132
|
}
|
|
131
|
-
export type FeatureStatus = 'backlog' | '
|
|
133
|
+
export type FeatureStatus = 'backlog' | 'ready_for_ai' | 'feature_analysis' | 'feature_analysis_verification' | 'technical_design' | 'technical_design_verification' | 'code_implementation' | 'code_implementation_verification' | 'code_refine' | 'code_refine_verification' | 'bug_fixing' | 'code_review' | 'pull_request' | 'functional_testing' | 'ready_for_review' | 'shipped' | 'testing_in_progress' | 'testing_passed' | 'testing_failed';
|