edsger 0.51.0 → 0.53.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 (188) hide show
  1. package/.claude/settings.local.json +23 -3
  2. package/.env.local +12 -0
  3. package/dist/commands/find-smells/index.d.ts +21 -0
  4. package/dist/commands/find-smells/index.js +65 -0
  5. package/dist/index.js +29 -0
  6. package/dist/phases/find-bugs/index.js +7 -92
  7. package/dist/phases/find-bugs/state.d.ts +10 -35
  8. package/dist/phases/find-bugs/state.js +12 -120
  9. package/dist/phases/find-features/index.js +16 -83
  10. package/dist/phases/find-features/prompts.d.ts +7 -1
  11. package/dist/phases/find-features/prompts.js +31 -11
  12. package/dist/phases/find-features/state.d.ts +15 -19
  13. package/dist/phases/find-features/state.js +17 -89
  14. package/dist/phases/find-features/types.d.ts +1 -1
  15. package/dist/phases/find-shared/git.d.ts +24 -0
  16. package/dist/phases/find-shared/git.js +60 -0
  17. package/dist/phases/find-shared/mcp.d.ts +33 -0
  18. package/dist/phases/find-shared/mcp.js +69 -0
  19. package/dist/phases/find-shared/scan-state.d.ts +33 -0
  20. package/dist/phases/find-shared/scan-state.js +112 -0
  21. package/dist/phases/find-smells/index.d.ts +47 -0
  22. package/dist/phases/find-smells/index.js +278 -0
  23. package/dist/phases/find-smells/prompts.d.ts +30 -0
  24. package/dist/phases/find-smells/prompts.js +129 -0
  25. package/dist/phases/find-smells/state.d.ts +21 -0
  26. package/dist/phases/find-smells/state.js +17 -0
  27. package/dist/phases/find-smells/types.d.ts +51 -0
  28. package/dist/phases/find-smells/types.js +64 -0
  29. package/dist/phases/pr-execution/context.js +40 -32
  30. package/dist/phases/pr-splitting/context.js +18 -13
  31. package/dist/utils/github-repo-info.d.ts +13 -1
  32. package/dist/utils/github-repo-info.js +32 -6
  33. package/package.json +1 -1
  34. package/vitest.config.ts +2 -0
  35. package/dist/api/__tests__/app-store.test.d.ts +0 -7
  36. package/dist/api/__tests__/app-store.test.js +0 -60
  37. package/dist/api/__tests__/intelligence.test.d.ts +0 -11
  38. package/dist/api/__tests__/intelligence.test.js +0 -315
  39. package/dist/api/features/__tests__/feature-utils.test.d.ts +0 -4
  40. package/dist/api/features/__tests__/feature-utils.test.js +0 -370
  41. package/dist/api/features/__tests__/status-updater.test.d.ts +0 -4
  42. package/dist/api/features/__tests__/status-updater.test.js +0 -88
  43. package/dist/api/features/approval-checker.d.ts +0 -20
  44. package/dist/api/features/approval-checker.js +0 -152
  45. package/dist/api/features/batch-operations.d.ts +0 -17
  46. package/dist/api/features/batch-operations.js +0 -100
  47. package/dist/api/features/feature-utils.d.ts +0 -23
  48. package/dist/api/features/feature-utils.js +0 -80
  49. package/dist/api/features/get-feature.d.ts +0 -5
  50. package/dist/api/features/get-feature.js +0 -21
  51. package/dist/api/features/index.d.ts +0 -8
  52. package/dist/api/features/index.js +0 -10
  53. package/dist/api/features/status-updater.d.ts +0 -41
  54. package/dist/api/features/status-updater.js +0 -122
  55. package/dist/api/features/test-cases.d.ts +0 -29
  56. package/dist/api/features/test-cases.js +0 -110
  57. package/dist/api/features/update-feature.d.ts +0 -20
  58. package/dist/api/features/update-feature.js +0 -83
  59. package/dist/api/features/user-stories.d.ts +0 -21
  60. package/dist/api/features/user-stories.js +0 -88
  61. package/dist/commands/agent-workflow/feature-worker.d.ts +0 -14
  62. package/dist/commands/agent-workflow/feature-worker.js +0 -65
  63. package/dist/commands/build/__tests__/build.test.d.ts +0 -5
  64. package/dist/commands/build/__tests__/build.test.js +0 -206
  65. package/dist/commands/build/__tests__/detect-project.test.d.ts +0 -6
  66. package/dist/commands/build/__tests__/detect-project.test.js +0 -160
  67. package/dist/commands/build/__tests__/run-build.test.d.ts +0 -6
  68. package/dist/commands/build/__tests__/run-build.test.js +0 -433
  69. package/dist/commands/intelligence/__tests__/command.test.d.ts +0 -4
  70. package/dist/commands/intelligence/__tests__/command.test.js +0 -48
  71. package/dist/commands/workflow/core/__tests__/feature-filter.test.d.ts +0 -5
  72. package/dist/commands/workflow/core/__tests__/feature-filter.test.js +0 -316
  73. package/dist/commands/workflow/core/__tests__/pipeline-evaluator.test.d.ts +0 -4
  74. package/dist/commands/workflow/core/__tests__/pipeline-evaluator.test.js +0 -397
  75. package/dist/commands/workflow/core/__tests__/state-manager.test.d.ts +0 -4
  76. package/dist/commands/workflow/core/__tests__/state-manager.test.js +0 -384
  77. package/dist/commands/workflow/core/feature-filter.d.ts +0 -16
  78. package/dist/commands/workflow/core/feature-filter.js +0 -47
  79. package/dist/commands/workflow/feature-coordinator.d.ts +0 -18
  80. package/dist/commands/workflow/feature-coordinator.js +0 -161
  81. package/dist/config/__tests__/config.test.d.ts +0 -4
  82. package/dist/config/__tests__/config.test.js +0 -286
  83. package/dist/config/__tests__/feature-status.test.d.ts +0 -4
  84. package/dist/config/__tests__/feature-status.test.js +0 -111
  85. package/dist/config/feature-status.d.ts +0 -56
  86. package/dist/config/feature-status.js +0 -130
  87. package/dist/errors/__tests__/index.test.d.ts +0 -4
  88. package/dist/errors/__tests__/index.test.js +0 -349
  89. package/dist/phases/app-store-generation/__tests__/agent.test.d.ts +0 -5
  90. package/dist/phases/app-store-generation/__tests__/agent.test.js +0 -142
  91. package/dist/phases/app-store-generation/__tests__/context.test.d.ts +0 -4
  92. package/dist/phases/app-store-generation/__tests__/context.test.js +0 -284
  93. package/dist/phases/app-store-generation/__tests__/prompts.test.d.ts +0 -4
  94. package/dist/phases/app-store-generation/__tests__/prompts.test.js +0 -122
  95. package/dist/phases/app-store-generation/__tests__/screenshot-composer.test.d.ts +0 -5
  96. package/dist/phases/app-store-generation/__tests__/screenshot-composer.test.js +0 -826
  97. package/dist/phases/code-review/__tests__/diff-utils.test.d.ts +0 -1
  98. package/dist/phases/code-review/__tests__/diff-utils.test.js +0 -101
  99. package/dist/phases/feature-analysis/agent.d.ts +0 -13
  100. package/dist/phases/feature-analysis/agent.js +0 -112
  101. package/dist/phases/feature-analysis/context.d.ts +0 -24
  102. package/dist/phases/feature-analysis/context.js +0 -138
  103. package/dist/phases/feature-analysis/index.d.ts +0 -8
  104. package/dist/phases/feature-analysis/index.js +0 -199
  105. package/dist/phases/feature-analysis/outcome.d.ts +0 -40
  106. package/dist/phases/feature-analysis/outcome.js +0 -280
  107. package/dist/phases/feature-analysis/prompts.d.ts +0 -10
  108. package/dist/phases/feature-analysis/prompts.js +0 -212
  109. package/dist/phases/feature-analysis-verification/agent.d.ts +0 -33
  110. package/dist/phases/feature-analysis-verification/agent.js +0 -124
  111. package/dist/phases/feature-analysis-verification/index.d.ts +0 -25
  112. package/dist/phases/feature-analysis-verification/index.js +0 -92
  113. package/dist/phases/feature-analysis-verification/prompts.d.ts +0 -10
  114. package/dist/phases/feature-analysis-verification/prompts.js +0 -100
  115. package/dist/phases/intelligence-analysis/__tests__/context.test.d.ts +0 -4
  116. package/dist/phases/intelligence-analysis/__tests__/context.test.js +0 -192
  117. package/dist/phases/intelligence-analysis/__tests__/matching.test.d.ts +0 -13
  118. package/dist/phases/intelligence-analysis/__tests__/matching.test.js +0 -154
  119. package/dist/phases/intelligence-analysis/__tests__/orchestration.test.d.ts +0 -5
  120. package/dist/phases/intelligence-analysis/__tests__/orchestration.test.js +0 -378
  121. package/dist/phases/intelligence-analysis/__tests__/prompts.test.d.ts +0 -4
  122. package/dist/phases/intelligence-analysis/__tests__/prompts.test.js +0 -33
  123. package/dist/phases/pr-execution/__tests__/file-assigner.test.d.ts +0 -1
  124. package/dist/phases/pr-execution/__tests__/file-assigner.test.js +0 -303
  125. package/dist/phases/pr-resolve/__tests__/checklist-learner.test.d.ts +0 -1
  126. package/dist/phases/pr-resolve/__tests__/checklist-learner.test.js +0 -157
  127. package/dist/phases/pr-resolve/__tests__/prompts.test.d.ts +0 -1
  128. package/dist/phases/pr-resolve/__tests__/prompts.test.js +0 -116
  129. package/dist/phases/pr-resolve/__tests__/resolve-mapping.test.d.ts +0 -1
  130. package/dist/phases/pr-resolve/__tests__/resolve-mapping.test.js +0 -138
  131. package/dist/phases/pr-resolve/__tests__/types.test.d.ts +0 -1
  132. package/dist/phases/pr-resolve/__tests__/types.test.js +0 -43
  133. package/dist/phases/pr-resolve/__tests__/workspace.test.d.ts +0 -1
  134. package/dist/phases/pr-resolve/__tests__/workspace.test.js +0 -111
  135. package/dist/phases/pr-review/__tests__/prompts.test.d.ts +0 -1
  136. package/dist/phases/pr-review/__tests__/prompts.test.js +0 -49
  137. package/dist/phases/pr-review/__tests__/review-comments.test.d.ts +0 -1
  138. package/dist/phases/pr-review/__tests__/review-comments.test.js +0 -110
  139. package/dist/phases/pr-shared/__tests__/agent-utils.test.d.ts +0 -1
  140. package/dist/phases/pr-shared/__tests__/agent-utils.test.js +0 -91
  141. package/dist/phases/pr-shared/__tests__/context.test.d.ts +0 -1
  142. package/dist/phases/pr-shared/__tests__/context.test.js +0 -94
  143. package/dist/phases/pr-splitting/__tests__/import-dep-validator.test.d.ts +0 -1
  144. package/dist/phases/pr-splitting/__tests__/import-dep-validator.test.js +0 -331
  145. package/dist/phases/run-sheet/render.d.ts +0 -60
  146. package/dist/phases/run-sheet/render.js +0 -297
  147. package/dist/phases/smoke-test/__tests__/agent.test.d.ts +0 -4
  148. package/dist/phases/smoke-test/__tests__/agent.test.js +0 -84
  149. package/dist/phases/smoke-test/__tests__/github.test.d.ts +0 -9
  150. package/dist/phases/smoke-test/__tests__/github.test.js +0 -120
  151. package/dist/phases/smoke-test/__tests__/snapshot.test.d.ts +0 -8
  152. package/dist/phases/smoke-test/__tests__/snapshot.test.js +0 -93
  153. package/dist/phases/smoke-test/github.d.ts +0 -54
  154. package/dist/phases/smoke-test/github.js +0 -101
  155. package/dist/phases/smoke-test/snapshot.d.ts +0 -27
  156. package/dist/phases/smoke-test/snapshot.js +0 -157
  157. package/dist/services/coaching/__tests__/coaching-agent.test.d.ts +0 -1
  158. package/dist/services/coaching/__tests__/coaching-agent.test.js +0 -74
  159. package/dist/services/coaching/__tests__/coaching-loop.test.d.ts +0 -1
  160. package/dist/services/coaching/__tests__/coaching-loop.test.js +0 -59
  161. package/dist/services/coaching/__tests__/self-rating.test.d.ts +0 -1
  162. package/dist/services/coaching/__tests__/self-rating.test.js +0 -188
  163. package/dist/services/lifecycle-agent/__tests__/phase-criteria.test.d.ts +0 -4
  164. package/dist/services/lifecycle-agent/__tests__/phase-criteria.test.js +0 -133
  165. package/dist/services/lifecycle-agent/__tests__/transition-rules.test.d.ts +0 -4
  166. package/dist/services/lifecycle-agent/__tests__/transition-rules.test.js +0 -336
  167. package/dist/services/lifecycle-agent/index.d.ts +0 -24
  168. package/dist/services/lifecycle-agent/index.js +0 -25
  169. package/dist/services/lifecycle-agent/phase-criteria.d.ts +0 -57
  170. package/dist/services/lifecycle-agent/phase-criteria.js +0 -335
  171. package/dist/services/lifecycle-agent/transition-rules.d.ts +0 -60
  172. package/dist/services/lifecycle-agent/transition-rules.js +0 -184
  173. package/dist/services/lifecycle-agent/types.d.ts +0 -190
  174. package/dist/services/lifecycle-agent/types.js +0 -12
  175. package/dist/services/phase-hooks/__tests__/bindings-fetcher.test.d.ts +0 -1
  176. package/dist/services/phase-hooks/__tests__/bindings-fetcher.test.js +0 -122
  177. package/dist/services/phase-hooks/__tests__/hook-executor.test.d.ts +0 -1
  178. package/dist/services/phase-hooks/__tests__/hook-executor.test.js +0 -321
  179. package/dist/services/phase-hooks/__tests__/hook-runner.test.d.ts +0 -1
  180. package/dist/services/phase-hooks/__tests__/hook-runner.test.js +0 -261
  181. package/dist/services/phase-hooks/__tests__/plugin-loader.test.d.ts +0 -1
  182. package/dist/services/phase-hooks/__tests__/plugin-loader.test.js +0 -158
  183. package/dist/services/video/__tests__/video-pipeline.test.d.ts +0 -6
  184. package/dist/services/video/__tests__/video-pipeline.test.js +0 -249
  185. package/dist/types/features.d.ts +0 -35
  186. package/dist/types/features.js +0 -1
  187. package/dist/workspace/__tests__/workspace-manager.test.d.ts +0 -7
  188. package/dist/workspace/__tests__/workspace-manager.test.js +0 -52
@@ -1,384 +0,0 @@
1
- /**
2
- * Unit tests for workflow state management utilities
3
- */
4
- import assert from 'node:assert';
5
- import { describe, it } from 'node:test';
6
- import { calculateStats, createCompletedState, createFailedState, createInitialState, createProcessingState, updateFeatureState, } from '../state-manager.js';
7
- void describe('State Manager', () => {
8
- void describe('createInitialState', () => {
9
- void it('should return an empty Map', () => {
10
- const state = createInitialState();
11
- assert.ok(state instanceof Map, 'Should return a Map instance');
12
- assert.strictEqual(state.size, 0, 'Map should be empty');
13
- });
14
- void it('should return a new Map on each call', () => {
15
- const state1 = createInitialState();
16
- const state2 = createInitialState();
17
- assert.notStrictEqual(state1, state2, 'Each call should return a distinct Map');
18
- });
19
- });
20
- void describe('createProcessingState', () => {
21
- void it('should create a new processing state with retryCount 1 when no current state', () => {
22
- const state = createProcessingState('feat-1');
23
- assert.strictEqual(state.featureId, 'feat-1');
24
- assert.strictEqual(state.retryCount, 1);
25
- assert.strictEqual(state.status, 'processing');
26
- assert.ok(state.lastAttempt instanceof Date, 'lastAttempt should be a Date');
27
- });
28
- void it('should increment retryCount when current state is provided', () => {
29
- const currentState = {
30
- featureId: 'feat-1',
31
- retryCount: 2,
32
- lastAttempt: new Date('2024-01-01'),
33
- status: 'failed',
34
- };
35
- const state = createProcessingState('feat-1', currentState);
36
- assert.strictEqual(state.retryCount, 3, 'Should increment retryCount by 1');
37
- assert.strictEqual(state.status, 'processing');
38
- });
39
- void it('should set a recent lastAttempt date', () => {
40
- const before = new Date();
41
- const state = createProcessingState('feat-1');
42
- const after = new Date();
43
- assert.ok(state.lastAttempt.getTime() >= before.getTime(), 'lastAttempt should be at or after the call');
44
- assert.ok(state.lastAttempt.getTime() <= after.getTime(), 'lastAttempt should be at or before now');
45
- });
46
- void it('should increment from retryCount 1 to 2', () => {
47
- const currentState = {
48
- featureId: 'feat-1',
49
- retryCount: 1,
50
- lastAttempt: new Date('2024-01-01'),
51
- status: 'completed',
52
- };
53
- const state = createProcessingState('feat-1', currentState);
54
- assert.strictEqual(state.retryCount, 2);
55
- });
56
- });
57
- void describe('createCompletedState', () => {
58
- void it('should create a completed state with retryCount 1 when no current state', () => {
59
- const state = createCompletedState('feat-2');
60
- assert.strictEqual(state.featureId, 'feat-2');
61
- assert.strictEqual(state.retryCount, 1);
62
- assert.strictEqual(state.status, 'completed');
63
- assert.ok(state.lastAttempt instanceof Date);
64
- });
65
- void it('should preserve retryCount from current state', () => {
66
- const currentState = {
67
- featureId: 'feat-2',
68
- retryCount: 5,
69
- lastAttempt: new Date('2024-01-01'),
70
- status: 'processing',
71
- };
72
- const state = createCompletedState('feat-2', currentState);
73
- assert.strictEqual(state.retryCount, 5, 'Should preserve the existing retryCount');
74
- assert.strictEqual(state.status, 'completed');
75
- });
76
- void it('should not increment retryCount (unlike createProcessingState)', () => {
77
- const currentState = {
78
- featureId: 'feat-2',
79
- retryCount: 3,
80
- lastAttempt: new Date('2024-01-01'),
81
- status: 'processing',
82
- };
83
- const state = createCompletedState('feat-2', currentState);
84
- assert.strictEqual(state.retryCount, 3, 'retryCount should stay the same, not increment');
85
- });
86
- void it('should update lastAttempt to current time', () => {
87
- const oldDate = new Date('2024-01-01');
88
- const currentState = {
89
- featureId: 'feat-2',
90
- retryCount: 1,
91
- lastAttempt: oldDate,
92
- status: 'processing',
93
- };
94
- const state = createCompletedState('feat-2', currentState);
95
- assert.ok(state.lastAttempt.getTime() > oldDate.getTime(), 'lastAttempt should be newer than the old date');
96
- });
97
- });
98
- void describe('createFailedState', () => {
99
- void it('should create a failed state with retryCount 1 when no current state', () => {
100
- const state = createFailedState('feat-3');
101
- assert.strictEqual(state.featureId, 'feat-3');
102
- assert.strictEqual(state.retryCount, 1);
103
- assert.strictEqual(state.status, 'failed');
104
- assert.ok(state.lastAttempt instanceof Date);
105
- });
106
- void it('should preserve retryCount from current state', () => {
107
- const currentState = {
108
- featureId: 'feat-3',
109
- retryCount: 4,
110
- lastAttempt: new Date('2024-01-01'),
111
- status: 'processing',
112
- };
113
- const state = createFailedState('feat-3', currentState);
114
- assert.strictEqual(state.retryCount, 4, 'Should preserve the existing retryCount');
115
- assert.strictEqual(state.status, 'failed');
116
- });
117
- void it('should not increment retryCount (unlike createProcessingState)', () => {
118
- const currentState = {
119
- featureId: 'feat-3',
120
- retryCount: 2,
121
- lastAttempt: new Date('2024-01-01'),
122
- status: 'processing',
123
- };
124
- const state = createFailedState('feat-3', currentState);
125
- assert.strictEqual(state.retryCount, 2);
126
- });
127
- });
128
- void describe('updateFeatureState', () => {
129
- void it('should return a new Map (immutability)', () => {
130
- const original = new Map();
131
- const updated = updateFeatureState(original, 'feat-1', () => createProcessingState('feat-1'));
132
- assert.notStrictEqual(updated, original, 'Should return a different Map instance');
133
- });
134
- void it('should not mutate the original Map', () => {
135
- const original = new Map();
136
- updateFeatureState(original, 'feat-1', () => createProcessingState('feat-1'));
137
- assert.strictEqual(original.size, 0, 'Original Map should remain empty');
138
- });
139
- void it('should add a new feature to the Map', () => {
140
- const states = new Map();
141
- const updated = updateFeatureState(states, 'feat-1', () => createProcessingState('feat-1'));
142
- assert.strictEqual(updated.size, 1, 'Map should have one entry');
143
- assert.strictEqual(updated.get('feat-1')?.status, 'processing');
144
- });
145
- void it('should update an existing feature in the Map', () => {
146
- const initial = new Map([
147
- [
148
- 'feat-1',
149
- {
150
- featureId: 'feat-1',
151
- retryCount: 1,
152
- lastAttempt: new Date('2024-01-01'),
153
- status: 'processing',
154
- },
155
- ],
156
- ]);
157
- const updated = updateFeatureState(initial, 'feat-1', (current) => createCompletedState('feat-1', current));
158
- assert.strictEqual(updated.get('feat-1')?.status, 'completed');
159
- assert.strictEqual(updated.get('feat-1')?.retryCount, 1, 'retryCount should be preserved');
160
- });
161
- void it('should pass current state to the update function', () => {
162
- const existingState = {
163
- featureId: 'feat-1',
164
- retryCount: 3,
165
- lastAttempt: new Date('2024-01-01'),
166
- status: 'processing',
167
- };
168
- const states = new Map([
169
- ['feat-1', existingState],
170
- ]);
171
- let receivedState;
172
- updateFeatureState(states, 'feat-1', (current) => {
173
- receivedState = current;
174
- return createCompletedState('feat-1', current);
175
- });
176
- assert.deepStrictEqual(receivedState, existingState, 'Update function should receive the current state');
177
- });
178
- void it('should pass undefined for non-existent feature', () => {
179
- const states = new Map();
180
- let receivedState = {
181
- featureId: 'sentinel',
182
- retryCount: 999,
183
- lastAttempt: new Date(),
184
- status: 'completed',
185
- };
186
- updateFeatureState(states, 'feat-new', (current) => {
187
- receivedState = current;
188
- return createProcessingState('feat-new', current);
189
- });
190
- assert.strictEqual(receivedState, undefined, 'Update function should receive undefined for non-existent feature');
191
- });
192
- void it('should preserve other features in the Map', () => {
193
- const states = new Map([
194
- [
195
- 'feat-1',
196
- {
197
- featureId: 'feat-1',
198
- retryCount: 1,
199
- lastAttempt: new Date('2024-01-01'),
200
- status: 'completed',
201
- },
202
- ],
203
- [
204
- 'feat-2',
205
- {
206
- featureId: 'feat-2',
207
- retryCount: 2,
208
- lastAttempt: new Date('2024-01-02'),
209
- status: 'processing',
210
- },
211
- ],
212
- ]);
213
- const updated = updateFeatureState(states, 'feat-2', (current) => createFailedState('feat-2', current));
214
- assert.strictEqual(updated.size, 2, 'Map should still have two entries');
215
- assert.strictEqual(updated.get('feat-1')?.status, 'completed', 'feat-1 should remain unchanged');
216
- assert.strictEqual(updated.get('feat-2')?.status, 'failed', 'feat-2 should be updated');
217
- });
218
- });
219
- void describe('calculateStats', () => {
220
- void it('should return zeros for an empty Map', () => {
221
- const states = new Map();
222
- const stats = calculateStats(states, false);
223
- assert.deepStrictEqual(stats, {
224
- totalProcessed: 0,
225
- completed: 0,
226
- failed: 0,
227
- processing: 0,
228
- isRunning: false,
229
- });
230
- });
231
- void it('should count all completed features', () => {
232
- const states = new Map([
233
- [
234
- 'feat-1',
235
- {
236
- featureId: 'feat-1',
237
- retryCount: 1,
238
- lastAttempt: new Date(),
239
- status: 'completed',
240
- },
241
- ],
242
- [
243
- 'feat-2',
244
- {
245
- featureId: 'feat-2',
246
- retryCount: 1,
247
- lastAttempt: new Date(),
248
- status: 'completed',
249
- },
250
- ],
251
- ]);
252
- const stats = calculateStats(states, true);
253
- assert.strictEqual(stats.totalProcessed, 2);
254
- assert.strictEqual(stats.completed, 2);
255
- assert.strictEqual(stats.failed, 0);
256
- assert.strictEqual(stats.processing, 0);
257
- assert.strictEqual(stats.isRunning, true);
258
- });
259
- void it('should count mixed states correctly', () => {
260
- const states = new Map([
261
- [
262
- 'feat-1',
263
- {
264
- featureId: 'feat-1',
265
- retryCount: 1,
266
- lastAttempt: new Date(),
267
- status: 'completed',
268
- },
269
- ],
270
- [
271
- 'feat-2',
272
- {
273
- featureId: 'feat-2',
274
- retryCount: 2,
275
- lastAttempt: new Date(),
276
- status: 'failed',
277
- },
278
- ],
279
- [
280
- 'feat-3',
281
- {
282
- featureId: 'feat-3',
283
- retryCount: 1,
284
- lastAttempt: new Date(),
285
- status: 'processing',
286
- },
287
- ],
288
- [
289
- 'feat-4',
290
- {
291
- featureId: 'feat-4',
292
- retryCount: 1,
293
- lastAttempt: new Date(),
294
- status: 'completed',
295
- },
296
- ],
297
- ]);
298
- const stats = calculateStats(states, true);
299
- assert.strictEqual(stats.totalProcessed, 4);
300
- assert.strictEqual(stats.completed, 2);
301
- assert.strictEqual(stats.failed, 1);
302
- assert.strictEqual(stats.processing, 1);
303
- assert.strictEqual(stats.isRunning, true);
304
- });
305
- void it('should reflect isRunning as false when stopped', () => {
306
- const states = new Map([
307
- [
308
- 'feat-1',
309
- {
310
- featureId: 'feat-1',
311
- retryCount: 1,
312
- lastAttempt: new Date(),
313
- status: 'completed',
314
- },
315
- ],
316
- ]);
317
- const stats = calculateStats(states, false);
318
- assert.strictEqual(stats.isRunning, false);
319
- });
320
- void it('should count all failed features', () => {
321
- const states = new Map([
322
- [
323
- 'feat-1',
324
- {
325
- featureId: 'feat-1',
326
- retryCount: 3,
327
- lastAttempt: new Date(),
328
- status: 'failed',
329
- },
330
- ],
331
- [
332
- 'feat-2',
333
- {
334
- featureId: 'feat-2',
335
- retryCount: 1,
336
- lastAttempt: new Date(),
337
- status: 'failed',
338
- },
339
- ],
340
- ]);
341
- const stats = calculateStats(states, false);
342
- assert.strictEqual(stats.totalProcessed, 2);
343
- assert.strictEqual(stats.completed, 0);
344
- assert.strictEqual(stats.failed, 2);
345
- assert.strictEqual(stats.processing, 0);
346
- });
347
- void it('should count all processing features', () => {
348
- const states = new Map([
349
- [
350
- 'feat-1',
351
- {
352
- featureId: 'feat-1',
353
- retryCount: 1,
354
- lastAttempt: new Date(),
355
- status: 'processing',
356
- },
357
- ],
358
- [
359
- 'feat-2',
360
- {
361
- featureId: 'feat-2',
362
- retryCount: 1,
363
- lastAttempt: new Date(),
364
- status: 'processing',
365
- },
366
- ],
367
- [
368
- 'feat-3',
369
- {
370
- featureId: 'feat-3',
371
- retryCount: 1,
372
- lastAttempt: new Date(),
373
- status: 'processing',
374
- },
375
- ],
376
- ]);
377
- const stats = calculateStats(states, true);
378
- assert.strictEqual(stats.totalProcessed, 3);
379
- assert.strictEqual(stats.completed, 0);
380
- assert.strictEqual(stats.failed, 0);
381
- assert.strictEqual(stats.processing, 3);
382
- });
383
- });
384
- });
@@ -1,16 +0,0 @@
1
- /**
2
- * Feature filtering utilities for workflow processor
3
- * Pure functions for filtering and selecting features for processing
4
- */
5
- import type { FeatureInfo } from '../../../types/features.js';
6
- import type { FeatureProcessingState } from './state-manager.js';
7
- export declare const shouldProcessFeature: (maxRetries: number) => (feature: FeatureInfo, states: Map<string, FeatureProcessingState>) => boolean;
8
- export declare const findNextFeature: (features: readonly FeatureInfo[], states: Map<string, FeatureProcessingState>, maxRetries: number) => FeatureInfo | undefined;
9
- export declare const isFeatureProcessing: (featureId: string, states: Map<string, FeatureProcessingState>) => boolean;
10
- export declare const isFeatureCompleted: (featureId: string, states: Map<string, FeatureProcessingState>) => boolean;
11
- export declare const isFeatureFailed: (featureId: string, states: Map<string, FeatureProcessingState>) => boolean;
12
- export declare const hasReachedMaxRetries: (featureId: string, states: Map<string, FeatureProcessingState>, maxRetries: number) => boolean;
13
- export declare const filterProcessingFeatures: (features: readonly FeatureInfo[], states: Map<string, FeatureProcessingState>) => FeatureInfo[];
14
- export declare const filterCompletedFeatures: (features: readonly FeatureInfo[], states: Map<string, FeatureProcessingState>) => FeatureInfo[];
15
- export declare const filterFailedFeatures: (features: readonly FeatureInfo[], states: Map<string, FeatureProcessingState>) => FeatureInfo[];
16
- export declare const filterAvailableFeatures: (features: readonly FeatureInfo[], states: Map<string, FeatureProcessingState>, maxRetries: number) => FeatureInfo[];
@@ -1,47 +0,0 @@
1
- /**
2
- * Feature filtering utilities for workflow processor
3
- * Pure functions for filtering and selecting features for processing
4
- */
5
- // Feature filtering functions (pure)
6
- export const shouldProcessFeature = (maxRetries) => (feature, states) => {
7
- const state = states.get(feature.id);
8
- // If never processed, should process
9
- if (!state) {
10
- return true;
11
- }
12
- // If feature was updated after last processing attempt, should reprocess
13
- // This handles cases where user manually changes status back to ready_for_ai
14
- if (feature.updated_at) {
15
- const featureUpdatedTime = new Date(feature.updated_at).getTime();
16
- const lastAttemptTime = state.lastAttempt.getTime();
17
- if (featureUpdatedTime > lastAttemptTime) {
18
- // Feature has been updated since last processing, reprocess it
19
- return true;
20
- }
21
- }
22
- // If failed and haven't exceeded retry limit, should retry
23
- return state.status === 'failed' && state.retryCount < maxRetries;
24
- };
25
- export const findNextFeature = (features, states, maxRetries) => features.find((feature) => shouldProcessFeature(maxRetries)(feature, states));
26
- // Feature status checking functions (pure)
27
- export const isFeatureProcessing = (featureId, states) => {
28
- const state = states.get(featureId);
29
- return state?.status === 'processing';
30
- };
31
- export const isFeatureCompleted = (featureId, states) => {
32
- const state = states.get(featureId);
33
- return state?.status === 'completed';
34
- };
35
- export const isFeatureFailed = (featureId, states) => {
36
- const state = states.get(featureId);
37
- return state?.status === 'failed';
38
- };
39
- export const hasReachedMaxRetries = (featureId, states, maxRetries) => {
40
- const state = states.get(featureId);
41
- return state ? state.retryCount >= maxRetries : false;
42
- };
43
- // Feature collection filtering functions (pure)
44
- export const filterProcessingFeatures = (features, states) => features.filter((feature) => isFeatureProcessing(feature.id, states));
45
- export const filterCompletedFeatures = (features, states) => features.filter((feature) => isFeatureCompleted(feature.id, states));
46
- export const filterFailedFeatures = (features, states) => features.filter((feature) => isFeatureFailed(feature.id, states));
47
- export const filterAvailableFeatures = (features, states, maxRetries) => features.filter((feature) => shouldProcessFeature(maxRetries)(feature, states));
@@ -1,18 +0,0 @@
1
- /**
2
- * Feature Coordinator - High-level workflow orchestration
3
- *
4
- * Responsibilities:
5
- * - Fetch feature configuration from API
6
- * - Execute phases based on workflow field (pending phases in order)
7
- * - Coordinate feature-level workflow initialization
8
- * - Handle phase execution results and stop on failure/blocked
9
- *
10
- * This is the entry point for feature workflow execution
11
- */
12
- import { type EdsgerConfig } from '../../types/index.js';
13
- import { type PipelinePhaseOptions, type PipelineResult } from '../../types/pipeline.js';
14
- /**
15
- * Run workflow for a feature based on its workflow field
16
- * Executes all pending phases in order, stopping on failure or blocked
17
- */
18
- export declare function runFeatureWorkflow(options: PipelinePhaseOptions, config: EdsgerConfig): Promise<readonly PipelineResult[]>;
@@ -1,161 +0,0 @@
1
- /**
2
- * Feature Coordinator - High-level workflow orchestration
3
- *
4
- * Responsibilities:
5
- * - Fetch feature configuration from API
6
- * - Execute phases based on workflow field (pending phases in order)
7
- * - Coordinate feature-level workflow initialization
8
- * - Handle phase execution results and stop on failure/blocked
9
- *
10
- * This is the entry point for feature workflow execution
11
- */
12
- import { getFeature } from '../../api/features/get-feature.js';
13
- import { markWorkflowPhaseCompleted } from '../../api/features/update-feature.js';
14
- import { cacheBindings, clearBindingsCache, fetchHookBindings, } from '../../services/phase-hooks/index.js';
15
- import { logError, logInfo, logWarning } from '../../utils/logger.js';
16
- import { logPhaseResult } from '../../utils/pipeline-logger.js';
17
- import { runAutonomousPhase, runBranchPlanningPhase, runCodeImplementationPhase, runCodeRefinePhase, runCodeReviewPhase, runFeatureAnalysisPhase, runFunctionalTestingPhase, runPRExecutionPhase, runPRSplittingPhase, runTechnicalDesignPhase, runTestCasesAnalysisPhase, runUserStoriesAnalysisPhase, } from './executors/phase-executor.js';
18
- /**
19
- * Map workflow phase names (underscore format) to phase runner functions
20
- * Note: code_refine includes built-in verification loop (similar to technical_design)
21
- */
22
- const PHASE_RUNNERS = {
23
- feature_analysis: runFeatureAnalysisPhase,
24
- user_stories_analysis: runUserStoriesAnalysisPhase,
25
- test_cases_analysis: runTestCasesAnalysisPhase,
26
- technical_design: runTechnicalDesignPhase,
27
- branch_planning: runBranchPlanningPhase,
28
- code_implementation: runCodeImplementationPhase,
29
- pr_splitting: runPRSplittingPhase,
30
- pr_execution: runPRExecutionPhase,
31
- functional_testing: runFunctionalTestingPhase,
32
- code_review: runCodeReviewPhase,
33
- code_refine: runCodeRefinePhase,
34
- autonomous: runAutonomousPhase,
35
- };
36
- /**
37
- * Get all pending phases from workflow in order
38
- */
39
- function getPendingPhases(workflow) {
40
- if (!workflow || workflow.length === 0) {
41
- return [];
42
- }
43
- return workflow.filter((phase) => phase.status === 'pending');
44
- }
45
- /**
46
- * Execute a single phase and handle results
47
- * Returns the result and marks workflow phase as completed if successful
48
- */
49
- async function executePhase(phaseName, options, config) {
50
- const { featureId, verbose } = options;
51
- // Normalize phase name: kebab-case → snake_case (e.g., code-implementation → code_implementation)
52
- const normalizedName = phaseName.replace(/-/g, '_');
53
- const phaseRunner = PHASE_RUNNERS[normalizedName];
54
- if (!phaseRunner) {
55
- logWarning(`Unknown workflow phase: ${phaseName}, skipping`);
56
- return {
57
- featureId,
58
- phase: phaseName,
59
- status: 'error',
60
- message: `Unknown workflow phase: ${phaseName}`,
61
- };
62
- }
63
- if (verbose) {
64
- logInfo(`\n🎯 Executing phase: ${normalizedName}`);
65
- }
66
- // Execute the phase
67
- const result = await phaseRunner(options, config);
68
- // Log the result
69
- logPhaseResult(result, verbose);
70
- // Mark workflow phase as completed if successful
71
- if (result.status === 'success') {
72
- await markWorkflowPhaseCompleted(featureId, normalizedName, verbose);
73
- }
74
- return result;
75
- }
76
- /**
77
- * Run workflow for a feature based on its workflow field
78
- * Executes all pending phases in order, stopping on failure or blocked
79
- */
80
- export async function runFeatureWorkflow(options, config) {
81
- const { featureId, verbose } = options;
82
- try {
83
- // 1. Get feature details to check workflow
84
- if (verbose) {
85
- logInfo(`🔍 Fetching feature configuration for: ${featureId}`);
86
- }
87
- const feature = await getFeature(featureId, verbose);
88
- // Fetch and cache hook bindings for this feature's product
89
- try {
90
- const bindings = await fetchHookBindings(feature.product_id, verbose);
91
- cacheBindings(featureId, feature.product_id, bindings);
92
- if (verbose && bindings.length > 0) {
93
- logInfo(`🪝 Loaded ${bindings.length} phase hook binding(s) for product ${feature.product_id}`);
94
- }
95
- }
96
- catch (hookError) {
97
- // Hook binding fetch failures should not block workflow execution
98
- logWarning(`Failed to fetch phase hook bindings: ${hookError instanceof Error ? hookError.message : String(hookError)}`);
99
- }
100
- // Debug log workflow data
101
- if (verbose) {
102
- logInfo(`🔍 Feature data from API:`);
103
- logInfo(` - workflow: ${JSON.stringify(feature.workflow, null, 2)}`);
104
- }
105
- // 2. Get all pending phases from workflow
106
- const pendingPhases = getPendingPhases(feature.workflow);
107
- if (pendingPhases.length === 0) {
108
- if (verbose) {
109
- logInfo('No pending phases in workflow, nothing to execute');
110
- }
111
- return [
112
- {
113
- featureId,
114
- phase: 'workflow-initialization',
115
- status: 'success',
116
- message: 'No pending phases in workflow, nothing to execute',
117
- },
118
- ];
119
- }
120
- if (verbose) {
121
- logInfo(`📋 Found ${pendingPhases.length} pending phases to execute:`);
122
- pendingPhases.forEach((p, i) => logInfo(` ${i + 1}. ${p.phase}`));
123
- }
124
- // 3. Execute each pending phase in order
125
- const results = [];
126
- for (const workflowPhase of pendingPhases) {
127
- const result = await executePhase(workflowPhase.phase, options, config);
128
- results.push(result);
129
- // Stop execution if phase failed or was blocked
130
- if (result.status === 'error' || result.status === 'blocked') {
131
- if (verbose) {
132
- logInfo(`\n⛔ Stopping workflow execution: ${workflowPhase.phase} ${result.status}`);
133
- }
134
- break;
135
- }
136
- // Note: PR creation is now handled within the code-implementation phase itself,
137
- // so we no longer need to create it here after code_implementation succeeds.
138
- }
139
- // Clean up hook bindings cache
140
- clearBindingsCache(featureId);
141
- if (verbose) {
142
- const successCount = results.filter((r) => r.status === 'success').length;
143
- logInfo(`\n✅ Workflow execution completed: ${successCount}/${results.length} phases succeeded`);
144
- }
145
- return results;
146
- }
147
- catch (error) {
148
- clearBindingsCache(featureId);
149
- const errorMessage = error instanceof Error ? error.message : String(error);
150
- logError(`Failed to run feature workflow: ${errorMessage}`);
151
- // Return error result
152
- return [
153
- {
154
- featureId,
155
- phase: 'workflow-initialization',
156
- status: 'error',
157
- message: `Workflow initialization failed: ${errorMessage}`,
158
- },
159
- ];
160
- }
161
- }
@@ -1,4 +0,0 @@
1
- /**
2
- * Unit tests for the config module (loadConfig and validateConfig)
3
- */
4
- export {};