edsger 0.23.0 → 0.24.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/commands/workflow/config/phase-configs.js +10 -0
- package/dist/commands/workflow/executors/phase-executor.d.ts +2 -2
- package/dist/commands/workflow/executors/phase-executor.js +2 -2
- package/dist/commands/workflow/phase-orchestrator.js +146 -2
- package/dist/config/feature-status.js +10 -0
- package/dist/phases/feature-analysis/index.js +0 -7
- package/dist/phases/technical-design/index.js +0 -7
- package/dist/phases/test-cases-analysis/agent.d.ts +2 -0
- package/dist/phases/test-cases-analysis/agent.js +102 -0
- package/dist/phases/test-cases-analysis/context.d.ts +14 -0
- package/dist/phases/test-cases-analysis/context.js +78 -0
- package/dist/phases/test-cases-analysis/formatters.d.ts +6 -0
- package/dist/phases/test-cases-analysis/formatters.js +35 -0
- package/dist/phases/test-cases-analysis/index.d.ts +9 -0
- package/dist/phases/test-cases-analysis/index.js +136 -0
- package/dist/phases/test-cases-analysis/outcome.d.ts +25 -0
- package/dist/phases/test-cases-analysis/outcome.js +114 -0
- package/dist/phases/test-cases-analysis/prompts.d.ts +6 -0
- package/dist/phases/test-cases-analysis/prompts.js +255 -0
- package/dist/phases/user-stories-analysis/agent.d.ts +2 -0
- package/dist/phases/user-stories-analysis/agent.js +102 -0
- package/dist/phases/user-stories-analysis/context.d.ts +14 -0
- package/dist/phases/user-stories-analysis/context.js +78 -0
- package/dist/phases/user-stories-analysis/formatters.d.ts +6 -0
- package/dist/phases/user-stories-analysis/formatters.js +32 -0
- package/dist/phases/user-stories-analysis/index.d.ts +9 -0
- package/dist/phases/user-stories-analysis/index.js +136 -0
- package/dist/phases/user-stories-analysis/outcome.d.ts +25 -0
- package/dist/phases/user-stories-analysis/outcome.js +114 -0
- package/dist/phases/user-stories-analysis/prompts.d.ts +6 -0
- package/dist/phases/user-stories-analysis/prompts.js +306 -0
- package/dist/services/audit-logs.d.ts +2 -2
- package/dist/types/index.d.ts +1 -1
- package/dist/types/pipeline.d.ts +1 -1
- package/package.json +1 -1
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* Phase configurations for the pipeline runner
|
|
3
3
|
*/
|
|
4
4
|
import { analyseFeature } from '../../../phases/feature-analysis/index.js';
|
|
5
|
+
import { analyseUserStories } from '../../../phases/user-stories-analysis/index.js';
|
|
6
|
+
import { analyseTestCases } from '../../../phases/test-cases-analysis/index.js';
|
|
5
7
|
import { generateTechnicalDesign } from '../../../phases/technical-design/index.js';
|
|
6
8
|
import { planFeatureBranches } from '../../../phases/branch-planning/index.js';
|
|
7
9
|
import { implementFeatureCode } from '../../../phases/code-implementation/index.js';
|
|
@@ -73,6 +75,14 @@ export const phaseConfigs = [
|
|
|
73
75
|
name: 'feature-analysis',
|
|
74
76
|
execute: analyseFeature,
|
|
75
77
|
},
|
|
78
|
+
{
|
|
79
|
+
name: 'user-stories-analysis',
|
|
80
|
+
execute: analyseUserStories,
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: 'test-cases-analysis',
|
|
84
|
+
execute: analyseTestCases,
|
|
85
|
+
},
|
|
76
86
|
{
|
|
77
87
|
name: 'technical-design',
|
|
78
88
|
execute: generateTechnicalDesign,
|
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
import { EdsgerConfig } from '../../../types/index.js';
|
|
5
5
|
import { PipelinePhaseOptions, PipelineResult, PhaseConfig } from '../../../types/pipeline.js';
|
|
6
6
|
export declare const createPhaseRunner: (phaseConfig: PhaseConfig) => (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>;
|
|
7
|
-
declare const runFeatureAnalysisPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runTechnicalDesignPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runBranchPlanningPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeImplementationPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runFunctionalTestingPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeTestingPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeRefinePhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeRefineVerificationPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeReviewPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runAutonomousPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>;
|
|
8
|
-
export { runFeatureAnalysisPhase, runTechnicalDesignPhase, runBranchPlanningPhase, runCodeImplementationPhase, runFunctionalTestingPhase, runCodeTestingPhase, runCodeRefinePhase, runCodeRefineVerificationPhase, runCodeReviewPhase, runAutonomousPhase, };
|
|
7
|
+
declare const runFeatureAnalysisPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runUserStoriesAnalysisPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runTestCasesAnalysisPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runTechnicalDesignPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runBranchPlanningPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeImplementationPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runFunctionalTestingPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeTestingPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeRefinePhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeRefineVerificationPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeReviewPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runAutonomousPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>;
|
|
8
|
+
export { runFeatureAnalysisPhase, runUserStoriesAnalysisPhase, runTestCasesAnalysisPhase, runTechnicalDesignPhase, runBranchPlanningPhase, runCodeImplementationPhase, runFunctionalTestingPhase, runCodeTestingPhase, runCodeRefinePhase, runCodeRefineVerificationPhase, runCodeReviewPhase, runAutonomousPhase, };
|
|
@@ -262,6 +262,6 @@ export const createPhaseRunner = (phaseConfig) => async (options, config) => {
|
|
|
262
262
|
}
|
|
263
263
|
};
|
|
264
264
|
// Create phase runners using the configuration
|
|
265
|
-
const [runFeatureAnalysisPhase, runTechnicalDesignPhase, runBranchPlanningPhase, runCodeImplementationPhase, runFunctionalTestingPhase, runCodeTestingPhase, runCodeRefinePhase, runCodeRefineVerificationPhase, runCodeReviewPhase, runAutonomousPhase,] = phaseConfigs.map(createPhaseRunner);
|
|
265
|
+
const [runFeatureAnalysisPhase, runUserStoriesAnalysisPhase, runTestCasesAnalysisPhase, runTechnicalDesignPhase, runBranchPlanningPhase, runCodeImplementationPhase, runFunctionalTestingPhase, runCodeTestingPhase, runCodeRefinePhase, runCodeRefineVerificationPhase, runCodeReviewPhase, runAutonomousPhase,] = phaseConfigs.map(createPhaseRunner);
|
|
266
266
|
// Export individual phase runners for granular control
|
|
267
|
-
export { runFeatureAnalysisPhase, runTechnicalDesignPhase, runBranchPlanningPhase, runCodeImplementationPhase, runFunctionalTestingPhase, runCodeTestingPhase, runCodeRefinePhase, runCodeRefineVerificationPhase, runCodeReviewPhase, runAutonomousPhase, };
|
|
267
|
+
export { runFeatureAnalysisPhase, runUserStoriesAnalysisPhase, runTestCasesAnalysisPhase, runTechnicalDesignPhase, runBranchPlanningPhase, runCodeImplementationPhase, runFunctionalTestingPhase, runCodeTestingPhase, runCodeRefinePhase, runCodeRefineVerificationPhase, runCodeReviewPhase, runAutonomousPhase, };
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* Uses functional programming principles for composability
|
|
15
15
|
*/
|
|
16
16
|
import { updateFeatureStatusForPhase, markWorkflowPhaseCompleted, } from '../../api/features/index.js';
|
|
17
|
-
import { runFeatureAnalysisPhase, runTechnicalDesignPhase, runCodeImplementationPhase, runFunctionalTestingPhase, runCodeTestingPhase, runCodeReviewPhase, } from './executors/phase-executor.js';
|
|
17
|
+
import { runFeatureAnalysisPhase, runUserStoriesAnalysisPhase, runTestCasesAnalysisPhase, runTechnicalDesignPhase, runCodeImplementationPhase, runFunctionalTestingPhase, runCodeTestingPhase, runCodeReviewPhase, } from './executors/phase-executor.js';
|
|
18
18
|
import { logPipelineStart, logPhaseResult, logPipelineComplete, shouldContinuePipeline, } from '../../utils/pipeline-logger.js';
|
|
19
19
|
import { handleTestFailuresWithRetry } from '../../phases/functional-testing/test-retry-handler.js';
|
|
20
20
|
import { handleCodeRefineWithRetry } from '../../phases/code-refine/retry-handler.js';
|
|
@@ -52,12 +52,22 @@ export const runPipelineByMode = async (options, config, executionMode) => {
|
|
|
52
52
|
logInfo(` ➡️ Matched: full_pipeline or from_feature_analysis - running complete pipeline`);
|
|
53
53
|
}
|
|
54
54
|
return await runFromFeatureAnalysis(options, config);
|
|
55
|
-
// Feature Analysis
|
|
55
|
+
// Feature Analysis (legacy - generates both user stories and test cases)
|
|
56
56
|
case 'only_feature_analysis':
|
|
57
57
|
if (verbose) {
|
|
58
58
|
logInfo(` ➡️ Matched: only_feature_analysis - running analysis only`);
|
|
59
59
|
}
|
|
60
60
|
return await runOnlyFeatureAnalysis(options, config);
|
|
61
|
+
// User Stories Analysis
|
|
62
|
+
case 'only_user_stories_analysis':
|
|
63
|
+
return await runOnlyUserStoriesAnalysis(options, config);
|
|
64
|
+
case 'from_user_stories_analysis':
|
|
65
|
+
return await runFromUserStoriesAnalysis(options, config);
|
|
66
|
+
// Test Cases Analysis
|
|
67
|
+
case 'only_test_cases_analysis':
|
|
68
|
+
return await runOnlyTestCasesAnalysis(options, config);
|
|
69
|
+
case 'from_test_cases_analysis':
|
|
70
|
+
return await runFromTestCasesAnalysis(options, config);
|
|
61
71
|
// Technical Design
|
|
62
72
|
case 'only_technical_design':
|
|
63
73
|
return await runOnlyTechnicalDesign(options, config);
|
|
@@ -120,6 +130,140 @@ const runOnlyFeatureAnalysis = async (options, config) => {
|
|
|
120
130
|
results.push(await logAndMarkPhaseCompleted(analysisResult, verbose));
|
|
121
131
|
return finalizePipelineExecution(options, results, verbose);
|
|
122
132
|
};
|
|
133
|
+
/**
|
|
134
|
+
* Run only user stories analysis phase
|
|
135
|
+
*/
|
|
136
|
+
const runOnlyUserStoriesAnalysis = async (options, config) => {
|
|
137
|
+
const { featureId, verbose } = options;
|
|
138
|
+
logPipelineStart(featureId, verbose);
|
|
139
|
+
const results = [];
|
|
140
|
+
const analysisResult = await runUserStoriesAnalysisPhase(options, config);
|
|
141
|
+
results.push(await logAndMarkPhaseCompleted(analysisResult, verbose));
|
|
142
|
+
return finalizePipelineExecution(options, results, verbose);
|
|
143
|
+
};
|
|
144
|
+
/**
|
|
145
|
+
* Run only test cases analysis phase
|
|
146
|
+
*/
|
|
147
|
+
const runOnlyTestCasesAnalysis = async (options, config) => {
|
|
148
|
+
const { featureId, verbose } = options;
|
|
149
|
+
logPipelineStart(featureId, verbose);
|
|
150
|
+
const results = [];
|
|
151
|
+
const analysisResult = await runTestCasesAnalysisPhase(options, config);
|
|
152
|
+
results.push(await logAndMarkPhaseCompleted(analysisResult, verbose));
|
|
153
|
+
return finalizePipelineExecution(options, results, verbose);
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* Run from user stories analysis to end
|
|
157
|
+
* Flow: user-stories-analysis → test-cases-analysis → technical-design → code-implementation → ...
|
|
158
|
+
*/
|
|
159
|
+
const runFromUserStoriesAnalysis = async (options, config) => {
|
|
160
|
+
const { featureId, verbose } = options;
|
|
161
|
+
logPipelineStart(featureId, verbose);
|
|
162
|
+
const results = [];
|
|
163
|
+
// 1. User Stories Analysis
|
|
164
|
+
const userStoriesResult = await runUserStoriesAnalysisPhase(options, config);
|
|
165
|
+
results.push(await logAndMarkPhaseCompleted(userStoriesResult, verbose));
|
|
166
|
+
if (!shouldContinuePipeline(results)) {
|
|
167
|
+
return finalizePipelineExecution(options, results, verbose);
|
|
168
|
+
}
|
|
169
|
+
// 2. Test Cases Analysis
|
|
170
|
+
const testCasesResult = await runTestCasesAnalysisPhase(options, config);
|
|
171
|
+
results.push(await logAndMarkPhaseCompleted(testCasesResult, verbose));
|
|
172
|
+
if (!shouldContinuePipeline(results)) {
|
|
173
|
+
return finalizePipelineExecution(options, results, verbose);
|
|
174
|
+
}
|
|
175
|
+
// 3. Technical Design
|
|
176
|
+
const designResult = await runTechnicalDesignPhase(options, config);
|
|
177
|
+
results.push(await logAndMarkPhaseCompleted(designResult, verbose));
|
|
178
|
+
if (!shouldContinuePipeline(results)) {
|
|
179
|
+
return finalizePipelineExecution(options, results, verbose);
|
|
180
|
+
}
|
|
181
|
+
// 4. Code Implementation
|
|
182
|
+
const implementationResult = await runCodeImplementationPhase(options, config);
|
|
183
|
+
results.push(await logAndMarkPhaseCompleted(implementationResult, verbose));
|
|
184
|
+
if (!shouldContinuePipeline(results)) {
|
|
185
|
+
return finalizePipelineExecution(options, results, verbose);
|
|
186
|
+
}
|
|
187
|
+
// 5. Functional Testing with retry loop for bug fixes
|
|
188
|
+
const testingResult = await handleTestFailuresWithRetry({
|
|
189
|
+
options,
|
|
190
|
+
config,
|
|
191
|
+
results,
|
|
192
|
+
verbose,
|
|
193
|
+
});
|
|
194
|
+
const finalStatus = testingResult.status === 'success' ? 'testing_passed' : 'testing_failed';
|
|
195
|
+
await updateFeatureStatusForPhase(featureId, finalStatus === 'testing_passed' ? 'testing-passed' : 'testing-failed', verbose);
|
|
196
|
+
if (testingResult.status === 'success') {
|
|
197
|
+
const prCreated = await handlePullRequestCreation({
|
|
198
|
+
featureId,
|
|
199
|
+
results,
|
|
200
|
+
testingResult,
|
|
201
|
+
verbose,
|
|
202
|
+
});
|
|
203
|
+
if (prCreated) {
|
|
204
|
+
await continueWithCodeReviewAndRefine(options, config, results, verbose);
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
if (verbose) {
|
|
208
|
+
logInfo('⚠️ Skipping code review and refine workflow: pull request creation failed');
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return finalizePipelineExecution(options, results, verbose);
|
|
213
|
+
};
|
|
214
|
+
/**
|
|
215
|
+
* Run from test cases analysis to end
|
|
216
|
+
* Flow: test-cases-analysis → technical-design → code-implementation → ...
|
|
217
|
+
*/
|
|
218
|
+
const runFromTestCasesAnalysis = async (options, config) => {
|
|
219
|
+
const { featureId, verbose } = options;
|
|
220
|
+
logPipelineStart(featureId, verbose);
|
|
221
|
+
const results = [];
|
|
222
|
+
// 1. Test Cases Analysis
|
|
223
|
+
const testCasesResult = await runTestCasesAnalysisPhase(options, config);
|
|
224
|
+
results.push(await logAndMarkPhaseCompleted(testCasesResult, verbose));
|
|
225
|
+
if (!shouldContinuePipeline(results)) {
|
|
226
|
+
return finalizePipelineExecution(options, results, verbose);
|
|
227
|
+
}
|
|
228
|
+
// 2. Technical Design
|
|
229
|
+
const designResult = await runTechnicalDesignPhase(options, config);
|
|
230
|
+
results.push(await logAndMarkPhaseCompleted(designResult, verbose));
|
|
231
|
+
if (!shouldContinuePipeline(results)) {
|
|
232
|
+
return finalizePipelineExecution(options, results, verbose);
|
|
233
|
+
}
|
|
234
|
+
// 3. Code Implementation
|
|
235
|
+
const implementationResult = await runCodeImplementationPhase(options, config);
|
|
236
|
+
results.push(await logAndMarkPhaseCompleted(implementationResult, verbose));
|
|
237
|
+
if (!shouldContinuePipeline(results)) {
|
|
238
|
+
return finalizePipelineExecution(options, results, verbose);
|
|
239
|
+
}
|
|
240
|
+
// 4. Functional Testing with retry loop for bug fixes
|
|
241
|
+
const testingResult = await handleTestFailuresWithRetry({
|
|
242
|
+
options,
|
|
243
|
+
config,
|
|
244
|
+
results,
|
|
245
|
+
verbose,
|
|
246
|
+
});
|
|
247
|
+
const finalStatus = testingResult.status === 'success' ? 'testing_passed' : 'testing_failed';
|
|
248
|
+
await updateFeatureStatusForPhase(featureId, finalStatus === 'testing_passed' ? 'testing-passed' : 'testing-failed', verbose);
|
|
249
|
+
if (testingResult.status === 'success') {
|
|
250
|
+
const prCreated = await handlePullRequestCreation({
|
|
251
|
+
featureId,
|
|
252
|
+
results,
|
|
253
|
+
testingResult,
|
|
254
|
+
verbose,
|
|
255
|
+
});
|
|
256
|
+
if (prCreated) {
|
|
257
|
+
await continueWithCodeReviewAndRefine(options, config, results, verbose);
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
if (verbose) {
|
|
261
|
+
logInfo('⚠️ Skipping code review and refine workflow: pull request creation failed');
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return finalizePipelineExecution(options, results, verbose);
|
|
266
|
+
};
|
|
123
267
|
/**
|
|
124
268
|
* Run only technical design phase
|
|
125
269
|
*/
|
|
@@ -26,6 +26,10 @@ export const STATUS_PROGRESSION_ORDER = [
|
|
|
26
26
|
'assigned_to_ai',
|
|
27
27
|
'feature_analysis',
|
|
28
28
|
'feature_analysis_verification',
|
|
29
|
+
'user_stories_analysis',
|
|
30
|
+
'user_stories_analysis_verification',
|
|
31
|
+
'test_cases_analysis',
|
|
32
|
+
'test_cases_analysis_verification',
|
|
29
33
|
'technical_design',
|
|
30
34
|
'technical_design_verification',
|
|
31
35
|
'branch_planning',
|
|
@@ -59,6 +63,10 @@ export const STATUS_PROGRESSION_ORDER = [
|
|
|
59
63
|
export const PHASE_STATUS_MAP = {
|
|
60
64
|
'feature-analysis': 'feature_analysis',
|
|
61
65
|
'feature-analysis-verification': 'feature_analysis_verification',
|
|
66
|
+
'user-stories-analysis': 'user_stories_analysis',
|
|
67
|
+
'user-stories-analysis-verification': 'user_stories_analysis_verification',
|
|
68
|
+
'test-cases-analysis': 'test_cases_analysis',
|
|
69
|
+
'test-cases-analysis-verification': 'test_cases_analysis_verification',
|
|
62
70
|
'technical-design': 'technical_design',
|
|
63
71
|
'technical-design-verification': 'technical_design_verification',
|
|
64
72
|
'branch-planning': 'branch_planning',
|
|
@@ -92,6 +100,8 @@ export const HUMAN_SELECTABLE_STATUSES = [
|
|
|
92
100
|
'backlog',
|
|
93
101
|
'ready_for_ai',
|
|
94
102
|
'feature_analysis',
|
|
103
|
+
'user_stories_analysis',
|
|
104
|
+
'test_cases_analysis',
|
|
95
105
|
'technical_design',
|
|
96
106
|
'branch_planning',
|
|
97
107
|
'code_implementation',
|
|
@@ -5,14 +5,11 @@ import { executeAnalysisQuery } from './agent.js';
|
|
|
5
5
|
import { performVerificationCycle } from '../feature-analysis-verification/index.js';
|
|
6
6
|
import { deleteArtifacts, deleteSpecificArtifacts, updateArtifactsToReady, saveAnalysisArtifactsAsDraft, buildAnalysisResult, } from './outcome.js';
|
|
7
7
|
import { logFeaturePhaseEvent } from '../../services/audit-logs.js';
|
|
8
|
-
import { preparePhaseGitEnvironment } from '../../utils/git-branch-manager.js';
|
|
9
8
|
export const analyseFeature = async (options, config, checklistContext) => {
|
|
10
9
|
const { featureId, verbose } = options;
|
|
11
10
|
if (verbose) {
|
|
12
11
|
logInfo(`Starting feature analysis for feature ID: ${featureId}`);
|
|
13
12
|
}
|
|
14
|
-
// Prepare git environment: switch to feature branch and rebase with main
|
|
15
|
-
const cleanupGit = preparePhaseGitEnvironment(featureId, 'main', verbose);
|
|
16
13
|
try {
|
|
17
14
|
const context = await prepareAnalysisContext(featureId, checklistContext, verbose);
|
|
18
15
|
const systemPrompt = createFeatureAnalysisSystemPrompt();
|
|
@@ -157,8 +154,4 @@ export const analyseFeature = async (options, config, checklistContext) => {
|
|
|
157
154
|
status: 'error',
|
|
158
155
|
};
|
|
159
156
|
}
|
|
160
|
-
finally {
|
|
161
|
-
// Always return to main branch after phase completion
|
|
162
|
-
cleanupGit();
|
|
163
|
-
}
|
|
164
157
|
};
|
|
@@ -10,7 +10,6 @@ import { getFeedbacksForPhase, formatFeedbacksForContext, } from '../../services
|
|
|
10
10
|
import { buildDesignResult, buildVerificationFailureResult, buildNoResultsError, } from './outcome.js';
|
|
11
11
|
import { performVerificationCycle } from '../technical-design-verification/index.js';
|
|
12
12
|
import { logFeaturePhaseEvent } from '../../services/audit-logs.js';
|
|
13
|
-
import { preparePhaseGitEnvironment } from '../../utils/git-branch-manager.js';
|
|
14
13
|
function userMessage(content) {
|
|
15
14
|
return {
|
|
16
15
|
type: 'user',
|
|
@@ -26,8 +25,6 @@ export const generateTechnicalDesign = async (options, config, checklistContext)
|
|
|
26
25
|
if (verbose) {
|
|
27
26
|
logInfo(`Starting technical design generation for feature ID: ${featureId}`);
|
|
28
27
|
}
|
|
29
|
-
// Prepare git environment: switch to feature branch and rebase with main
|
|
30
|
-
const cleanupGit = preparePhaseGitEnvironment(featureId, 'main', verbose);
|
|
31
28
|
try {
|
|
32
29
|
// Fetch and prepare context
|
|
33
30
|
const context = await prepareDesignContext(featureId, checklistContext, verbose);
|
|
@@ -143,10 +140,6 @@ export const generateTechnicalDesign = async (options, config, checklistContext)
|
|
|
143
140
|
summary: `Generation failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
144
141
|
};
|
|
145
142
|
}
|
|
146
|
-
finally {
|
|
147
|
-
// Always return to main branch after phase completion
|
|
148
|
-
cleanupGit();
|
|
149
|
-
}
|
|
150
143
|
};
|
|
151
144
|
/**
|
|
152
145
|
* Prepare all context information needed for design
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { query } from '@anthropic-ai/claude-agent-sdk';
|
|
2
|
+
import { DEFAULT_MODEL } from '../../constants.js';
|
|
3
|
+
import { logInfo, logError } from '../../utils/logger.js';
|
|
4
|
+
function parseAnalysisResult(responseText) {
|
|
5
|
+
try {
|
|
6
|
+
let jsonResult = null;
|
|
7
|
+
const jsonBlockMatch = responseText.match(/```json\s*\n([\s\S]*?)\n\s*```/);
|
|
8
|
+
if (jsonBlockMatch) {
|
|
9
|
+
jsonResult = JSON.parse(jsonBlockMatch[1]);
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
jsonResult = JSON.parse(responseText);
|
|
13
|
+
}
|
|
14
|
+
if (jsonResult && jsonResult.analysis) {
|
|
15
|
+
return { analysis: jsonResult.analysis };
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
return { error: 'Invalid JSON structure' };
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
return {
|
|
23
|
+
error: `JSON parsing failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function userMessage(content) {
|
|
28
|
+
return {
|
|
29
|
+
type: 'user',
|
|
30
|
+
message: { role: 'user', content: content },
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
async function* prompt(analysisPrompt) {
|
|
34
|
+
yield userMessage(analysisPrompt);
|
|
35
|
+
}
|
|
36
|
+
export async function executeTestCasesAnalysisQuery(currentPrompt, systemPrompt, config, verbose) {
|
|
37
|
+
let lastAssistantResponse = '';
|
|
38
|
+
let structuredAnalysisResult = null;
|
|
39
|
+
for await (const message of query({
|
|
40
|
+
prompt: prompt(currentPrompt),
|
|
41
|
+
options: {
|
|
42
|
+
systemPrompt: {
|
|
43
|
+
type: 'preset',
|
|
44
|
+
preset: 'claude_code',
|
|
45
|
+
append: systemPrompt,
|
|
46
|
+
},
|
|
47
|
+
model: DEFAULT_MODEL,
|
|
48
|
+
maxTurns: 1000,
|
|
49
|
+
permissionMode: 'bypassPermissions',
|
|
50
|
+
},
|
|
51
|
+
})) {
|
|
52
|
+
if (verbose) {
|
|
53
|
+
logInfo(`Received message type: ${message.type}`);
|
|
54
|
+
}
|
|
55
|
+
if (message.type === 'assistant' && message.message?.content) {
|
|
56
|
+
for (const content of message.message.content) {
|
|
57
|
+
if (content.type === 'text') {
|
|
58
|
+
lastAssistantResponse += content.text + '\n';
|
|
59
|
+
if (verbose) {
|
|
60
|
+
console.log(`\n🤖 ${content.text}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
else if (content.type === 'tool_use') {
|
|
64
|
+
if (verbose) {
|
|
65
|
+
console.log(`\n🔧 ${content.name}: ${content.input.description || 'Running...'}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (message.type === 'result') {
|
|
71
|
+
if (message.subtype === 'success') {
|
|
72
|
+
logInfo('\n📊 Test cases analysis completed, parsing results...');
|
|
73
|
+
const responseText = message.result || lastAssistantResponse;
|
|
74
|
+
const parsed = parseAnalysisResult(responseText);
|
|
75
|
+
if (parsed.error) {
|
|
76
|
+
logError(`Failed to parse structured analysis result: ${parsed.error}`);
|
|
77
|
+
structuredAnalysisResult = {
|
|
78
|
+
status: 'error',
|
|
79
|
+
summary: 'Failed to parse analysis results from Claude Code response',
|
|
80
|
+
created_test_cases: [],
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
structuredAnalysisResult = parsed.analysis;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
logError(`\n⚠️ Analysis incomplete: ${message.subtype}`);
|
|
89
|
+
if (message.subtype === 'error_max_turns') {
|
|
90
|
+
logError('💡 Try increasing timeout or reducing complexity');
|
|
91
|
+
}
|
|
92
|
+
if (lastAssistantResponse) {
|
|
93
|
+
const parsed = parseAnalysisResult(lastAssistantResponse);
|
|
94
|
+
if (!parsed.error) {
|
|
95
|
+
structuredAnalysisResult = parsed.analysis;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return structuredAnalysisResult;
|
|
102
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { FeatureInfo, UserStory, TestCase } from '../../types/features.js';
|
|
2
|
+
import { type ProductInfo } from '../../api/products.js';
|
|
3
|
+
import { ChecklistPhaseContext } from '../../services/checklist.js';
|
|
4
|
+
export interface TestCasesAnalysisContext {
|
|
5
|
+
feature: FeatureInfo;
|
|
6
|
+
product: ProductInfo;
|
|
7
|
+
existing_user_stories: UserStory[];
|
|
8
|
+
existing_test_cases: TestCase[];
|
|
9
|
+
}
|
|
10
|
+
export declare function fetchTestCasesAnalysisContext(featureId: string, verbose?: boolean): Promise<TestCasesAnalysisContext>;
|
|
11
|
+
export declare function prepareTestCasesAnalysisContext(featureId: string, checklistContext: ChecklistPhaseContext | null | undefined, verbose?: boolean): Promise<{
|
|
12
|
+
featureContext: TestCasesAnalysisContext;
|
|
13
|
+
analysisPrompt: string;
|
|
14
|
+
}>;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { logInfo, logError } from '../../utils/logger.js';
|
|
2
|
+
import { getFeature, getUserStories, getTestCases, } from '../../api/features/index.js';
|
|
3
|
+
import { getProduct } from '../../api/products.js';
|
|
4
|
+
import { formatChecklistsForContext, } from '../../services/checklist.js';
|
|
5
|
+
import { getFeedbacksForPhase, formatFeedbacksForContext, } from '../../services/feedbacks.js';
|
|
6
|
+
import { formatTestCasesAnalysisContext } from './formatters.js';
|
|
7
|
+
import { createTestCasesAnalysisPromptWithContext } from './prompts.js';
|
|
8
|
+
export async function fetchTestCasesAnalysisContext(featureId, verbose) {
|
|
9
|
+
try {
|
|
10
|
+
if (verbose) {
|
|
11
|
+
logInfo(`Fetching test cases analysis context for feature: ${featureId}`);
|
|
12
|
+
}
|
|
13
|
+
const [feature, existing_user_stories, existing_test_cases] = await Promise.all([
|
|
14
|
+
getFeature(featureId, verbose),
|
|
15
|
+
getUserStories(featureId, verbose),
|
|
16
|
+
getTestCases(featureId, verbose),
|
|
17
|
+
]);
|
|
18
|
+
const product = await getProduct(feature.product_id, verbose);
|
|
19
|
+
if (verbose) {
|
|
20
|
+
logInfo(`✅ Test cases analysis context fetched successfully:`);
|
|
21
|
+
logInfo(` Feature: ${feature.name}`);
|
|
22
|
+
logInfo(` Product: ${product.name}`);
|
|
23
|
+
logInfo(` User Stories: ${existing_user_stories.length}`);
|
|
24
|
+
logInfo(` Existing Test Cases: ${existing_test_cases.length}`);
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
feature,
|
|
28
|
+
product,
|
|
29
|
+
existing_user_stories,
|
|
30
|
+
existing_test_cases,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
35
|
+
logError(`Failed to fetch test cases analysis context: ${errorMessage}`);
|
|
36
|
+
throw new Error(`Context fetch failed: ${errorMessage}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export async function prepareTestCasesAnalysisContext(featureId, checklistContext, verbose) {
|
|
40
|
+
if (verbose) {
|
|
41
|
+
logInfo('Fetching test cases analysis context via MCP endpoints...');
|
|
42
|
+
}
|
|
43
|
+
const featureContext = await fetchTestCasesAnalysisContext(featureId, verbose);
|
|
44
|
+
const { content: contextInfo, downloadedImages } = await formatTestCasesAnalysisContext(featureContext);
|
|
45
|
+
if (verbose && downloadedImages.length > 0) {
|
|
46
|
+
logInfo(`Downloaded ${downloadedImages.length} images for Claude Code:`);
|
|
47
|
+
downloadedImages.forEach((img) => {
|
|
48
|
+
logInfo(` - ${img.url} -> ${img.localPath}`);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
let finalContextInfo = contextInfo;
|
|
52
|
+
// Add feedbacks context
|
|
53
|
+
try {
|
|
54
|
+
const feedbacksContext = await getFeedbacksForPhase({ featureId, verbose }, 'test-cases-analysis');
|
|
55
|
+
if (feedbacksContext.feedbacks.length > 0) {
|
|
56
|
+
const feedbacksInfo = await formatFeedbacksForContext(feedbacksContext);
|
|
57
|
+
finalContextInfo = finalContextInfo + '\n\n' + feedbacksInfo;
|
|
58
|
+
if (verbose) {
|
|
59
|
+
logInfo(`Added ${feedbacksContext.feedbacks.length} human feedbacks to context`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
if (verbose) {
|
|
65
|
+
logInfo(`Note: Could not fetch feedbacks (${error instanceof Error ? error.message : String(error)})`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Add checklist context
|
|
69
|
+
if (checklistContext && checklistContext.checklists.length > 0) {
|
|
70
|
+
const checklistInfo = formatChecklistsForContext(checklistContext);
|
|
71
|
+
finalContextInfo = finalContextInfo + '\n\n' + checklistInfo;
|
|
72
|
+
if (verbose) {
|
|
73
|
+
logInfo(`Added ${checklistContext.checklists.length} checklists to analysis context`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const analysisPrompt = createTestCasesAnalysisPromptWithContext(featureId, finalContextInfo);
|
|
77
|
+
return { featureContext, analysisPrompt };
|
|
78
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type DownloadedImage } from '../../utils/image-downloader.js';
|
|
2
|
+
import { TestCasesAnalysisContext } from './context.js';
|
|
3
|
+
export declare function formatTestCasesAnalysisContext(context: TestCasesAnalysisContext): Promise<{
|
|
4
|
+
content: string;
|
|
5
|
+
downloadedImages: DownloadedImage[];
|
|
6
|
+
}>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { downloadImagesForClaudeCode, } from '../../utils/image-downloader.js';
|
|
2
|
+
import { formatUserStories, formatTestCases } from '../../utils/formatters.js';
|
|
3
|
+
export async function formatTestCasesAnalysisContext(context) {
|
|
4
|
+
const { processedMarkdown, downloadedImages } = await downloadImagesForClaudeCode(context.feature.description || 'No description provided', context.feature.id);
|
|
5
|
+
const content = `# Test Cases Analysis Context
|
|
6
|
+
|
|
7
|
+
## Feature Information
|
|
8
|
+
- **ID**: ${context.feature.id}
|
|
9
|
+
- **Name**: ${context.feature.name}
|
|
10
|
+
- **Description**:
|
|
11
|
+
${processedMarkdown}
|
|
12
|
+
|
|
13
|
+
${downloadedImages.length > 0 ? '**IMPORTANT**: The description contains images that have been downloaded locally. Please use the Read tool to view these images to fully understand the requirements.' : ''}
|
|
14
|
+
|
|
15
|
+
- **Current Status**: ${context.feature.status}
|
|
16
|
+
|
|
17
|
+
## Product Information
|
|
18
|
+
- **Product**: ${context.product.name}
|
|
19
|
+
- **Product ID**: ${context.product.id}
|
|
20
|
+
- **Description**: ${context.product.description || 'No product description'}
|
|
21
|
+
|
|
22
|
+
## User Stories (${context.existing_user_stories.length})
|
|
23
|
+
${formatUserStories(context.existing_user_stories)}
|
|
24
|
+
|
|
25
|
+
## Existing Test Cases (${context.existing_test_cases.length})
|
|
26
|
+
${formatTestCases(context.existing_test_cases)}
|
|
27
|
+
|
|
28
|
+
## Current Technical Design
|
|
29
|
+
${context.feature.technical_design || 'No technical design available yet'}
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
**Analysis Instructions**: Based on the above feature information, user stories, and existing test cases, create comprehensive test cases that ensure thorough testing coverage.`;
|
|
34
|
+
return { content, downloadedImages };
|
|
35
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { EdsgerConfig } from '../../types/index.js';
|
|
2
|
+
import { ChecklistPhaseContext } from '../../services/checklist.js';
|
|
3
|
+
import { TestCasesAnalysisResult } from './outcome.js';
|
|
4
|
+
export interface TestCasesAnalysisOptions {
|
|
5
|
+
featureId: string;
|
|
6
|
+
verbose?: boolean;
|
|
7
|
+
maxVerificationIterations?: number;
|
|
8
|
+
}
|
|
9
|
+
export declare const analyseTestCases: (options: TestCasesAnalysisOptions, config: EdsgerConfig, checklistContext?: ChecklistPhaseContext | null) => Promise<TestCasesAnalysisResult>;
|