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.
Files changed (35) hide show
  1. package/dist/commands/workflow/config/phase-configs.js +10 -0
  2. package/dist/commands/workflow/executors/phase-executor.d.ts +2 -2
  3. package/dist/commands/workflow/executors/phase-executor.js +2 -2
  4. package/dist/commands/workflow/phase-orchestrator.js +146 -2
  5. package/dist/config/feature-status.js +10 -0
  6. package/dist/phases/feature-analysis/index.js +0 -7
  7. package/dist/phases/technical-design/index.js +0 -7
  8. package/dist/phases/test-cases-analysis/agent.d.ts +2 -0
  9. package/dist/phases/test-cases-analysis/agent.js +102 -0
  10. package/dist/phases/test-cases-analysis/context.d.ts +14 -0
  11. package/dist/phases/test-cases-analysis/context.js +78 -0
  12. package/dist/phases/test-cases-analysis/formatters.d.ts +6 -0
  13. package/dist/phases/test-cases-analysis/formatters.js +35 -0
  14. package/dist/phases/test-cases-analysis/index.d.ts +9 -0
  15. package/dist/phases/test-cases-analysis/index.js +136 -0
  16. package/dist/phases/test-cases-analysis/outcome.d.ts +25 -0
  17. package/dist/phases/test-cases-analysis/outcome.js +114 -0
  18. package/dist/phases/test-cases-analysis/prompts.d.ts +6 -0
  19. package/dist/phases/test-cases-analysis/prompts.js +255 -0
  20. package/dist/phases/user-stories-analysis/agent.d.ts +2 -0
  21. package/dist/phases/user-stories-analysis/agent.js +102 -0
  22. package/dist/phases/user-stories-analysis/context.d.ts +14 -0
  23. package/dist/phases/user-stories-analysis/context.js +78 -0
  24. package/dist/phases/user-stories-analysis/formatters.d.ts +6 -0
  25. package/dist/phases/user-stories-analysis/formatters.js +32 -0
  26. package/dist/phases/user-stories-analysis/index.d.ts +9 -0
  27. package/dist/phases/user-stories-analysis/index.js +136 -0
  28. package/dist/phases/user-stories-analysis/outcome.d.ts +25 -0
  29. package/dist/phases/user-stories-analysis/outcome.js +114 -0
  30. package/dist/phases/user-stories-analysis/prompts.d.ts +6 -0
  31. package/dist/phases/user-stories-analysis/prompts.js +306 -0
  32. package/dist/services/audit-logs.d.ts +2 -2
  33. package/dist/types/index.d.ts +1 -1
  34. package/dist/types/pipeline.d.ts +1 -1
  35. 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,2 @@
1
+ import { EdsgerConfig } from '../../types/index.js';
2
+ export declare function executeTestCasesAnalysisQuery(currentPrompt: string, systemPrompt: string, config: EdsgerConfig, verbose?: boolean): Promise<any | null>;
@@ -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>;