edsger 0.51.0 → 0.52.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 (184) 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/package.json +1 -1
  30. package/vitest.config.ts +2 -0
  31. package/dist/api/__tests__/app-store.test.d.ts +0 -7
  32. package/dist/api/__tests__/app-store.test.js +0 -60
  33. package/dist/api/__tests__/intelligence.test.d.ts +0 -11
  34. package/dist/api/__tests__/intelligence.test.js +0 -315
  35. package/dist/api/features/__tests__/feature-utils.test.d.ts +0 -4
  36. package/dist/api/features/__tests__/feature-utils.test.js +0 -370
  37. package/dist/api/features/__tests__/status-updater.test.d.ts +0 -4
  38. package/dist/api/features/__tests__/status-updater.test.js +0 -88
  39. package/dist/api/features/approval-checker.d.ts +0 -20
  40. package/dist/api/features/approval-checker.js +0 -152
  41. package/dist/api/features/batch-operations.d.ts +0 -17
  42. package/dist/api/features/batch-operations.js +0 -100
  43. package/dist/api/features/feature-utils.d.ts +0 -23
  44. package/dist/api/features/feature-utils.js +0 -80
  45. package/dist/api/features/get-feature.d.ts +0 -5
  46. package/dist/api/features/get-feature.js +0 -21
  47. package/dist/api/features/index.d.ts +0 -8
  48. package/dist/api/features/index.js +0 -10
  49. package/dist/api/features/status-updater.d.ts +0 -41
  50. package/dist/api/features/status-updater.js +0 -122
  51. package/dist/api/features/test-cases.d.ts +0 -29
  52. package/dist/api/features/test-cases.js +0 -110
  53. package/dist/api/features/update-feature.d.ts +0 -20
  54. package/dist/api/features/update-feature.js +0 -83
  55. package/dist/api/features/user-stories.d.ts +0 -21
  56. package/dist/api/features/user-stories.js +0 -88
  57. package/dist/commands/agent-workflow/feature-worker.d.ts +0 -14
  58. package/dist/commands/agent-workflow/feature-worker.js +0 -65
  59. package/dist/commands/build/__tests__/build.test.d.ts +0 -5
  60. package/dist/commands/build/__tests__/build.test.js +0 -206
  61. package/dist/commands/build/__tests__/detect-project.test.d.ts +0 -6
  62. package/dist/commands/build/__tests__/detect-project.test.js +0 -160
  63. package/dist/commands/build/__tests__/run-build.test.d.ts +0 -6
  64. package/dist/commands/build/__tests__/run-build.test.js +0 -433
  65. package/dist/commands/intelligence/__tests__/command.test.d.ts +0 -4
  66. package/dist/commands/intelligence/__tests__/command.test.js +0 -48
  67. package/dist/commands/workflow/core/__tests__/feature-filter.test.d.ts +0 -5
  68. package/dist/commands/workflow/core/__tests__/feature-filter.test.js +0 -316
  69. package/dist/commands/workflow/core/__tests__/pipeline-evaluator.test.d.ts +0 -4
  70. package/dist/commands/workflow/core/__tests__/pipeline-evaluator.test.js +0 -397
  71. package/dist/commands/workflow/core/__tests__/state-manager.test.d.ts +0 -4
  72. package/dist/commands/workflow/core/__tests__/state-manager.test.js +0 -384
  73. package/dist/commands/workflow/core/feature-filter.d.ts +0 -16
  74. package/dist/commands/workflow/core/feature-filter.js +0 -47
  75. package/dist/commands/workflow/feature-coordinator.d.ts +0 -18
  76. package/dist/commands/workflow/feature-coordinator.js +0 -161
  77. package/dist/config/__tests__/config.test.d.ts +0 -4
  78. package/dist/config/__tests__/config.test.js +0 -286
  79. package/dist/config/__tests__/feature-status.test.d.ts +0 -4
  80. package/dist/config/__tests__/feature-status.test.js +0 -111
  81. package/dist/config/feature-status.d.ts +0 -56
  82. package/dist/config/feature-status.js +0 -130
  83. package/dist/errors/__tests__/index.test.d.ts +0 -4
  84. package/dist/errors/__tests__/index.test.js +0 -349
  85. package/dist/phases/app-store-generation/__tests__/agent.test.d.ts +0 -5
  86. package/dist/phases/app-store-generation/__tests__/agent.test.js +0 -142
  87. package/dist/phases/app-store-generation/__tests__/context.test.d.ts +0 -4
  88. package/dist/phases/app-store-generation/__tests__/context.test.js +0 -284
  89. package/dist/phases/app-store-generation/__tests__/prompts.test.d.ts +0 -4
  90. package/dist/phases/app-store-generation/__tests__/prompts.test.js +0 -122
  91. package/dist/phases/app-store-generation/__tests__/screenshot-composer.test.d.ts +0 -5
  92. package/dist/phases/app-store-generation/__tests__/screenshot-composer.test.js +0 -826
  93. package/dist/phases/code-review/__tests__/diff-utils.test.d.ts +0 -1
  94. package/dist/phases/code-review/__tests__/diff-utils.test.js +0 -101
  95. package/dist/phases/feature-analysis/agent.d.ts +0 -13
  96. package/dist/phases/feature-analysis/agent.js +0 -112
  97. package/dist/phases/feature-analysis/context.d.ts +0 -24
  98. package/dist/phases/feature-analysis/context.js +0 -138
  99. package/dist/phases/feature-analysis/index.d.ts +0 -8
  100. package/dist/phases/feature-analysis/index.js +0 -199
  101. package/dist/phases/feature-analysis/outcome.d.ts +0 -40
  102. package/dist/phases/feature-analysis/outcome.js +0 -280
  103. package/dist/phases/feature-analysis/prompts.d.ts +0 -10
  104. package/dist/phases/feature-analysis/prompts.js +0 -212
  105. package/dist/phases/feature-analysis-verification/agent.d.ts +0 -33
  106. package/dist/phases/feature-analysis-verification/agent.js +0 -124
  107. package/dist/phases/feature-analysis-verification/index.d.ts +0 -25
  108. package/dist/phases/feature-analysis-verification/index.js +0 -92
  109. package/dist/phases/feature-analysis-verification/prompts.d.ts +0 -10
  110. package/dist/phases/feature-analysis-verification/prompts.js +0 -100
  111. package/dist/phases/intelligence-analysis/__tests__/context.test.d.ts +0 -4
  112. package/dist/phases/intelligence-analysis/__tests__/context.test.js +0 -192
  113. package/dist/phases/intelligence-analysis/__tests__/matching.test.d.ts +0 -13
  114. package/dist/phases/intelligence-analysis/__tests__/matching.test.js +0 -154
  115. package/dist/phases/intelligence-analysis/__tests__/orchestration.test.d.ts +0 -5
  116. package/dist/phases/intelligence-analysis/__tests__/orchestration.test.js +0 -378
  117. package/dist/phases/intelligence-analysis/__tests__/prompts.test.d.ts +0 -4
  118. package/dist/phases/intelligence-analysis/__tests__/prompts.test.js +0 -33
  119. package/dist/phases/pr-execution/__tests__/file-assigner.test.d.ts +0 -1
  120. package/dist/phases/pr-execution/__tests__/file-assigner.test.js +0 -303
  121. package/dist/phases/pr-resolve/__tests__/checklist-learner.test.d.ts +0 -1
  122. package/dist/phases/pr-resolve/__tests__/checklist-learner.test.js +0 -157
  123. package/dist/phases/pr-resolve/__tests__/prompts.test.d.ts +0 -1
  124. package/dist/phases/pr-resolve/__tests__/prompts.test.js +0 -116
  125. package/dist/phases/pr-resolve/__tests__/resolve-mapping.test.d.ts +0 -1
  126. package/dist/phases/pr-resolve/__tests__/resolve-mapping.test.js +0 -138
  127. package/dist/phases/pr-resolve/__tests__/types.test.d.ts +0 -1
  128. package/dist/phases/pr-resolve/__tests__/types.test.js +0 -43
  129. package/dist/phases/pr-resolve/__tests__/workspace.test.d.ts +0 -1
  130. package/dist/phases/pr-resolve/__tests__/workspace.test.js +0 -111
  131. package/dist/phases/pr-review/__tests__/prompts.test.d.ts +0 -1
  132. package/dist/phases/pr-review/__tests__/prompts.test.js +0 -49
  133. package/dist/phases/pr-review/__tests__/review-comments.test.d.ts +0 -1
  134. package/dist/phases/pr-review/__tests__/review-comments.test.js +0 -110
  135. package/dist/phases/pr-shared/__tests__/agent-utils.test.d.ts +0 -1
  136. package/dist/phases/pr-shared/__tests__/agent-utils.test.js +0 -91
  137. package/dist/phases/pr-shared/__tests__/context.test.d.ts +0 -1
  138. package/dist/phases/pr-shared/__tests__/context.test.js +0 -94
  139. package/dist/phases/pr-splitting/__tests__/import-dep-validator.test.d.ts +0 -1
  140. package/dist/phases/pr-splitting/__tests__/import-dep-validator.test.js +0 -331
  141. package/dist/phases/run-sheet/render.d.ts +0 -60
  142. package/dist/phases/run-sheet/render.js +0 -297
  143. package/dist/phases/smoke-test/__tests__/agent.test.d.ts +0 -4
  144. package/dist/phases/smoke-test/__tests__/agent.test.js +0 -84
  145. package/dist/phases/smoke-test/__tests__/github.test.d.ts +0 -9
  146. package/dist/phases/smoke-test/__tests__/github.test.js +0 -120
  147. package/dist/phases/smoke-test/__tests__/snapshot.test.d.ts +0 -8
  148. package/dist/phases/smoke-test/__tests__/snapshot.test.js +0 -93
  149. package/dist/phases/smoke-test/github.d.ts +0 -54
  150. package/dist/phases/smoke-test/github.js +0 -101
  151. package/dist/phases/smoke-test/snapshot.d.ts +0 -27
  152. package/dist/phases/smoke-test/snapshot.js +0 -157
  153. package/dist/services/coaching/__tests__/coaching-agent.test.d.ts +0 -1
  154. package/dist/services/coaching/__tests__/coaching-agent.test.js +0 -74
  155. package/dist/services/coaching/__tests__/coaching-loop.test.d.ts +0 -1
  156. package/dist/services/coaching/__tests__/coaching-loop.test.js +0 -59
  157. package/dist/services/coaching/__tests__/self-rating.test.d.ts +0 -1
  158. package/dist/services/coaching/__tests__/self-rating.test.js +0 -188
  159. package/dist/services/lifecycle-agent/__tests__/phase-criteria.test.d.ts +0 -4
  160. package/dist/services/lifecycle-agent/__tests__/phase-criteria.test.js +0 -133
  161. package/dist/services/lifecycle-agent/__tests__/transition-rules.test.d.ts +0 -4
  162. package/dist/services/lifecycle-agent/__tests__/transition-rules.test.js +0 -336
  163. package/dist/services/lifecycle-agent/index.d.ts +0 -24
  164. package/dist/services/lifecycle-agent/index.js +0 -25
  165. package/dist/services/lifecycle-agent/phase-criteria.d.ts +0 -57
  166. package/dist/services/lifecycle-agent/phase-criteria.js +0 -335
  167. package/dist/services/lifecycle-agent/transition-rules.d.ts +0 -60
  168. package/dist/services/lifecycle-agent/transition-rules.js +0 -184
  169. package/dist/services/lifecycle-agent/types.d.ts +0 -190
  170. package/dist/services/lifecycle-agent/types.js +0 -12
  171. package/dist/services/phase-hooks/__tests__/bindings-fetcher.test.d.ts +0 -1
  172. package/dist/services/phase-hooks/__tests__/bindings-fetcher.test.js +0 -122
  173. package/dist/services/phase-hooks/__tests__/hook-executor.test.d.ts +0 -1
  174. package/dist/services/phase-hooks/__tests__/hook-executor.test.js +0 -321
  175. package/dist/services/phase-hooks/__tests__/hook-runner.test.d.ts +0 -1
  176. package/dist/services/phase-hooks/__tests__/hook-runner.test.js +0 -261
  177. package/dist/services/phase-hooks/__tests__/plugin-loader.test.d.ts +0 -1
  178. package/dist/services/phase-hooks/__tests__/plugin-loader.test.js +0 -158
  179. package/dist/services/video/__tests__/video-pipeline.test.d.ts +0 -6
  180. package/dist/services/video/__tests__/video-pipeline.test.js +0 -249
  181. package/dist/types/features.d.ts +0 -35
  182. package/dist/types/features.js +0 -1
  183. package/dist/workspace/__tests__/workspace-manager.test.d.ts +0 -7
  184. package/dist/workspace/__tests__/workspace-manager.test.js +0 -52
@@ -1,101 +0,0 @@
1
- import assert from 'node:assert';
2
- import { describe, it } from 'node:test';
3
- import { buildLineToPositionMap, findClosestPosition } from '../diff-utils.js';
4
- void describe('buildLineToPositionMap', () => {
5
- void it('maps simple additions correctly', () => {
6
- const patch = `@@ -1,3 +1,4 @@
7
- line 1
8
- +new line
9
- line 2
10
- line 3`;
11
- const map = buildLineToPositionMap(patch);
12
- // line 1 is at position 1 (new file line 1)
13
- assert.strictEqual(map.get(1), 1);
14
- // new line is at position 2 (new file line 2)
15
- assert.strictEqual(map.get(2), 2);
16
- // line 2 is at position 3 (new file line 3)
17
- assert.strictEqual(map.get(3), 3);
18
- // line 3 is at position 4 (new file line 4)
19
- assert.strictEqual(map.get(4), 4);
20
- });
21
- void it('handles deletions - deleted lines have no new file line number', () => {
22
- const patch = `@@ -1,3 +1,2 @@
23
- line 1
24
- -deleted line
25
- line 3`;
26
- const map = buildLineToPositionMap(patch);
27
- assert.strictEqual(map.get(1), 1);
28
- // deleted line takes position 2 but no new line
29
- // line 3 in old file becomes line 2 in new file, position 3
30
- assert.strictEqual(map.get(2), 3);
31
- });
32
- void it('handles multiple hunks', () => {
33
- const patch = `@@ -1,2 +1,2 @@
34
- line 1
35
- +added
36
- @@ -10,2 +10,2 @@
37
- line 10
38
- +added at 11`;
39
- const map = buildLineToPositionMap(patch);
40
- // First hunk: line 1 -> pos 1, added -> pos 2
41
- assert.strictEqual(map.get(1), 1);
42
- assert.strictEqual(map.get(2), 2);
43
- // Second hunk: line 10 -> pos 3, added at 11 -> pos 4
44
- assert.strictEqual(map.get(10), 3);
45
- assert.strictEqual(map.get(11), 4);
46
- });
47
- void it('returns empty map for patch with only hunk header', () => {
48
- const patch = '@@ -0,0 +1 @@';
49
- const map = buildLineToPositionMap(patch);
50
- // Hunk header only, no content lines
51
- assert.strictEqual(map.size, 0);
52
- });
53
- void it('handles hunk starting at line other than 1', () => {
54
- const patch = `@@ -50,3 +50,3 @@
55
- context
56
- -old
57
- +new`;
58
- const map = buildLineToPositionMap(patch);
59
- assert.strictEqual(map.get(50), 1);
60
- // -old takes position 2 (no new line)
61
- // +new takes position 3, new file line 51
62
- assert.strictEqual(map.get(51), 3);
63
- });
64
- });
65
- void describe('findClosestPosition', () => {
66
- void it('returns exact match when available', () => {
67
- const map = new Map([
68
- [10, 5],
69
- [11, 6],
70
- [12, 7],
71
- ]);
72
- const result = findClosestPosition(11, map);
73
- assert.deepStrictEqual(result, { position: 6, actualLine: 11 });
74
- });
75
- void it('returns nearby line below first', () => {
76
- const map = new Map([
77
- [10, 5],
78
- [15, 8],
79
- ]);
80
- // Line 12 not in map, closest below is 15 (offset 3)
81
- // closest above is 10 (offset 2) - but we check below first at each offset
82
- // offset 1: check 13 (no), check 11 (no)
83
- // offset 2: check 14 (no), check 10 (yes!)
84
- const result = findClosestPosition(12, map);
85
- assert.deepStrictEqual(result, { position: 5, actualLine: 10 });
86
- });
87
- void it('returns null when no line within range', () => {
88
- const map = new Map([
89
- [1, 1],
90
- [100, 50],
91
- ]);
92
- // Line 50 is too far from both 1 and 100
93
- const result = findClosestPosition(50, map);
94
- assert.strictEqual(result, null);
95
- });
96
- void it('returns null for empty map', () => {
97
- const map = new Map();
98
- const result = findClosestPosition(5, map);
99
- assert.strictEqual(result, null);
100
- });
101
- });
@@ -1,13 +0,0 @@
1
- import { type EdsgerConfig, type WithExecutionSessionId } from '../../types/index.js';
2
- interface ParsedAnalysisResult {
3
- analysis?: any;
4
- error?: string;
5
- }
6
- /** Analysis result with typed session ID */
7
- export type AnalysisQueryResult = Record<string, unknown> & WithExecutionSessionId;
8
- /**
9
- * Parse JSON result from Claude Code response
10
- */
11
- export declare function parseAnalysisResult(responseText: string): ParsedAnalysisResult;
12
- export declare function executeAnalysisQuery(currentPrompt: string, systemPrompt: string, config: EdsgerConfig, verbose?: boolean): Promise<AnalysisQueryResult | null>;
13
- export {};
@@ -1,112 +0,0 @@
1
- import { query } from '@anthropic-ai/claude-agent-sdk';
2
- import { DEFAULT_MODEL } from '../../constants.js';
3
- import { logDebug, logError, logInfo } from '../../utils/logger.js';
4
- /**
5
- * Parse JSON result from Claude Code response
6
- */
7
- export function parseAnalysisResult(responseText) {
8
- try {
9
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
10
- let jsonResult = null;
11
- // First try to extract JSON from markdown code block
12
- const jsonBlockMatch = responseText.match(/```json\s*\n([\s\S]*?)\n\s*```/);
13
- if (jsonBlockMatch) {
14
- jsonResult = JSON.parse(jsonBlockMatch[1]);
15
- }
16
- else {
17
- // Try to parse the entire response as JSON
18
- jsonResult = JSON.parse(responseText);
19
- }
20
- if (jsonResult && jsonResult.analysis) {
21
- return { analysis: jsonResult.analysis };
22
- }
23
- return { error: 'Invalid JSON structure' };
24
- }
25
- catch (error) {
26
- return {
27
- error: `JSON parsing failed: ${error instanceof Error ? error.message : String(error)}`,
28
- };
29
- }
30
- }
31
- function userMessage(content) {
32
- return {
33
- type: 'user',
34
- message: { role: 'user', content },
35
- };
36
- }
37
- // eslint-disable-next-line @typescript-eslint/require-await -- async generator required by SDK interface
38
- async function* prompt(analysisPrompt) {
39
- yield userMessage(analysisPrompt);
40
- }
41
- export async function executeAnalysisQuery(currentPrompt, systemPrompt, config, verbose) {
42
- let lastAssistantResponse = '';
43
- let structuredAnalysisResult = null;
44
- let executionSessionId = '';
45
- for await (const message of query({
46
- prompt: prompt(currentPrompt),
47
- options: {
48
- systemPrompt: {
49
- type: 'preset',
50
- preset: 'claude_code',
51
- append: systemPrompt,
52
- },
53
- model: DEFAULT_MODEL,
54
- maxTurns: 1000,
55
- permissionMode: 'bypassPermissions',
56
- },
57
- })) {
58
- if (verbose) {
59
- logInfo(`Received message type: ${message.type}`);
60
- }
61
- // Stream the analysis process and capture assistant responses
62
- if (message.type === 'assistant' && message.message?.content) {
63
- for (const content of message.message.content) {
64
- if (content.type === 'text') {
65
- lastAssistantResponse += `${content.text}\n`;
66
- logDebug(`🤖 ${content.text}`, verbose);
67
- }
68
- else if (content.type === 'tool_use') {
69
- logDebug(`🔧 ${content.name}: ${content.input.description || 'Running...'}`, verbose);
70
- }
71
- }
72
- }
73
- if (message.type === 'result') {
74
- executionSessionId = message.session_id || executionSessionId;
75
- if (message.subtype === 'success') {
76
- logInfo('\n📊 Feature analysis completed, parsing results...');
77
- const responseText = message.result || lastAssistantResponse;
78
- const parsed = parseAnalysisResult(responseText);
79
- if (parsed.error) {
80
- logError(`Failed to parse structured analysis result: ${parsed.error}`);
81
- structuredAnalysisResult = {
82
- status: 'error',
83
- summary: 'Failed to parse analysis results from Claude Code response',
84
- created_user_stories: [],
85
- created_test_cases: [],
86
- };
87
- }
88
- else {
89
- structuredAnalysisResult = parsed.analysis;
90
- }
91
- }
92
- else {
93
- logError(`\n⚠️ Analysis incomplete: ${message.subtype}`);
94
- if (message.subtype === 'error_max_turns') {
95
- logError('💡 Try increasing timeout or reducing complexity');
96
- }
97
- // Try to parse results from the last assistant response
98
- if (lastAssistantResponse) {
99
- const parsed = parseAnalysisResult(lastAssistantResponse);
100
- if (!parsed.error) {
101
- structuredAnalysisResult = parsed.analysis;
102
- }
103
- }
104
- }
105
- }
106
- }
107
- if (structuredAnalysisResult) {
108
- structuredAnalysisResult.execution_session_id =
109
- executionSessionId || undefined;
110
- }
111
- return structuredAnalysisResult;
112
- }
@@ -1,24 +0,0 @@
1
- import { type ProductInfo } from '../../api/products.js';
2
- import { type ChecklistPhaseContext } from '../../services/checklist.js';
3
- import type { FeatureInfo, TestCase, UserStory } from '../../types/features.js';
4
- export interface FeatureAnalysisContext {
5
- feature: FeatureInfo;
6
- product: ProductInfo;
7
- existing_user_stories: UserStory[];
8
- existing_test_cases: TestCase[];
9
- }
10
- /**
11
- * Fetch all feature analysis context information via MCP endpoints
12
- */
13
- export declare function fetchFeatureAnalysisContext(featureId: string, verbose?: boolean): Promise<FeatureAnalysisContext>;
14
- /**
15
- * Format the context into a readable string for Claude Code
16
- */
17
- export declare function formatContextForPrompt(context: FeatureAnalysisContext): string;
18
- /**
19
- * Prepare all context information needed for analysis
20
- */
21
- export declare function prepareAnalysisContext(featureId: string, checklistContext: ChecklistPhaseContext | null | undefined, verbose?: boolean): Promise<{
22
- featureContext: FeatureAnalysisContext;
23
- analysisPrompt: string;
24
- }>;
@@ -1,138 +0,0 @@
1
- import { getFeature, getTestCases, getUserStories, } from '../../api/features/index.js';
2
- import { getProduct } from '../../api/products.js';
3
- import { formatChecklistsForContext, } from '../../services/checklist.js';
4
- import { formatFeedbacksForContext, getFeedbacksForPhase, } from '../../services/feedbacks.js';
5
- import { formatFeatureAnalysisContext } from '../../utils/formatters.js';
6
- import { logError, logInfo } from '../../utils/logger.js';
7
- import { createFeatureAnalysisPromptWithContext } from './prompts.js';
8
- /**
9
- * Fetch all feature analysis context information via MCP endpoints
10
- */
11
- export async function fetchFeatureAnalysisContext(featureId, verbose) {
12
- try {
13
- if (verbose) {
14
- logInfo(`Fetching complete feature analysis context for feature: ${featureId}`);
15
- }
16
- // Fetch all required data in parallel for better performance
17
- const [feature, existingUserStories, existingTestCases] = await Promise.all([
18
- getFeature(featureId, verbose),
19
- getUserStories(featureId, verbose),
20
- getTestCases(featureId, verbose),
21
- ]);
22
- const product = await getProduct(feature.product_id, verbose);
23
- if (verbose) {
24
- logInfo(`✅ Feature analysis context fetched successfully:`);
25
- logInfo(` Feature: ${feature.name}`);
26
- logInfo(` Product: ${product.name}`);
27
- logInfo(` Existing User Stories: ${existingUserStories.length}`);
28
- logInfo(` Existing Test Cases: ${existingTestCases.length}`);
29
- }
30
- return {
31
- feature,
32
- product,
33
- existing_user_stories: existingUserStories,
34
- existing_test_cases: existingTestCases,
35
- };
36
- }
37
- catch (error) {
38
- const errorMessage = error instanceof Error ? error.message : String(error);
39
- logError(`Failed to fetch feature analysis context: ${errorMessage}`);
40
- throw new Error(`Context fetch failed: ${errorMessage}`);
41
- }
42
- }
43
- /**
44
- * Format the context into a readable string for Claude Code
45
- */
46
- export function formatContextForPrompt(context) {
47
- const formatUserStories = (stories) => {
48
- if (stories.length === 0) {
49
- return 'No user stories defined.';
50
- }
51
- return stories
52
- .map((story, index) => `${index + 1}. **${story.title}** (Status: ${story.status})
53
- ID: ${story.id}${story.status === 'draft' ? ' [DELETABLE]' : ''}
54
- ${story.description}`)
55
- .join('\n\n');
56
- };
57
- const formatTestCases = (cases) => {
58
- if (cases.length === 0) {
59
- return 'No test cases defined.';
60
- }
61
- return cases
62
- .map((testCase, index) => {
63
- return `${index + 1}. **${testCase.name}** ${testCase.is_critical ? '[CRITICAL]' : '[OPTIONAL]'} (Status: ${testCase.status || 'unknown'})
64
- ID: ${testCase.id}${testCase.status === 'draft' ? ' [DELETABLE]' : ''}
65
- ${testCase.description}`;
66
- })
67
- .join('\n\n');
68
- };
69
- return `# Feature Analysis Context
70
-
71
- ## Feature Information
72
- - **ID**: ${context.feature.id}
73
- - **Name**: ${context.feature.name}
74
- - **Description**: ${context.feature.description || 'No description provided'}
75
- - **Current Status**: ${context.feature.status}
76
-
77
- ## Product Information
78
- - **Product**: ${context.product.name}
79
- - **Product ID**: ${context.product.id}
80
- - **Description**: ${context.product.description || 'No product description'}
81
-
82
- ## Existing User Stories (${context.existing_user_stories.length})
83
- ${formatUserStories(context.existing_user_stories)}
84
-
85
- ## Existing Test Cases (${context.existing_test_cases.length})
86
- ${formatTestCases(context.existing_test_cases)}
87
-
88
- ## Current Technical Design
89
- ${context.feature.technical_design || 'No technical design available yet'}
90
-
91
- ---
92
-
93
- **Analysis Instructions**: Based on the above feature information and existing user stories/test cases, conduct comprehensive business analysis to identify gaps and create additional user stories and test cases that add business value.`;
94
- }
95
- /**
96
- * Prepare all context information needed for analysis
97
- */
98
- export async function prepareAnalysisContext(featureId, checklistContext, verbose) {
99
- if (verbose) {
100
- logInfo('Fetching feature analysis context via MCP endpoints...');
101
- }
102
- const featureContext = await fetchFeatureAnalysisContext(featureId, verbose);
103
- const { content: contextInfo, downloadedImages } = await formatFeatureAnalysisContext(featureContext);
104
- if (verbose && downloadedImages.length > 0) {
105
- logInfo(`Downloaded ${downloadedImages.length} images for Claude Code:`);
106
- downloadedImages.forEach((img) => {
107
- logInfo(` - ${img.url} -> ${img.localPath}`);
108
- });
109
- }
110
- let finalContextInfo = contextInfo;
111
- // Add feedbacks context to the analysis prompt
112
- try {
113
- const feedbacksContext = await getFeedbacksForPhase({ featureId, verbose }, 'feature-analysis');
114
- if (feedbacksContext.feedbacks.length > 0) {
115
- const feedbacksInfo = await formatFeedbacksForContext(feedbacksContext);
116
- finalContextInfo = `${finalContextInfo}\n\n${feedbacksInfo}`;
117
- if (verbose) {
118
- logInfo(`Added ${feedbacksContext.feedbacks.length} human feedbacks to context`);
119
- }
120
- }
121
- }
122
- catch (error) {
123
- // Don't fail if feedbacks fetch fails - just log and continue
124
- if (verbose) {
125
- logInfo(`Note: Could not fetch feedbacks (${error instanceof Error ? error.message : String(error)})`);
126
- }
127
- }
128
- // Add checklist context to the analysis prompt
129
- if (checklistContext && checklistContext.checklists.length > 0) {
130
- const checklistInfo = formatChecklistsForContext(checklistContext);
131
- finalContextInfo = `${finalContextInfo}\n\n${checklistInfo}`;
132
- if (verbose) {
133
- logInfo(`Added ${checklistContext.checklists.length} checklists to analysis context`);
134
- }
135
- }
136
- const analysisPrompt = createFeatureAnalysisPromptWithContext(featureId, finalContextInfo);
137
- return { featureContext, analysisPrompt };
138
- }
@@ -1,8 +0,0 @@
1
- import { type ChecklistPhaseContext } from '../../services/checklist.js';
2
- import { type EdsgerConfig, type FeatureAnalysisResult } from '../../types/index.js';
3
- export interface FeatureAnalysisOptions {
4
- featureId: string;
5
- verbose?: boolean;
6
- maxVerificationIterations?: number;
7
- }
8
- export declare const analyseFeature: (options: FeatureAnalysisOptions, config: EdsgerConfig, checklistContext?: ChecklistPhaseContext | null) => Promise<FeatureAnalysisResult>;
@@ -1,199 +0,0 @@
1
- import { logFeaturePhaseEvent } from '../../services/audit-logs.js';
2
- import { extractChecklistItems, runPhaseCoaching, } from '../../services/coaching/index.js';
3
- import { logError, logInfo, logWarning } from '../../utils/logger.js';
4
- import { performVerificationCycle } from '../feature-analysis-verification/index.js';
5
- import { executeAnalysisQuery, parseAnalysisResult } from './agent.js';
6
- import { prepareAnalysisContext } from './context.js';
7
- import { buildAnalysisResult, deleteArtifacts, deleteSpecificArtifacts, getAllDraftArtifactIds, resetReadyArtifactsToDraft, saveAnalysisArtifactsAsDraft, updateArtifactsToReady, } from './outcome.js';
8
- import { createFeatureAnalysisSystemPrompt } from './prompts.js';
9
- // eslint-disable-next-line complexity
10
- export const analyseFeature = async (options, config, checklistContext) => {
11
- const { featureId, verbose } = options;
12
- if (verbose) {
13
- logInfo(`Starting feature analysis for feature ID: ${featureId}`);
14
- }
15
- try {
16
- // Reset pending_approval artifacts to draft so AI can manage them on re-run
17
- const resetResult = await resetReadyArtifactsToDraft(featureId, verbose);
18
- if (verbose &&
19
- (resetResult.resetUserStories > 0 || resetResult.resetTestCases > 0)) {
20
- logInfo(`✅ Reset ${resetResult.resetUserStories} user stories and ${resetResult.resetTestCases} test cases to draft for re-analysis`);
21
- }
22
- const context = await prepareAnalysisContext(featureId, checklistContext, verbose);
23
- const systemPrompt = await createFeatureAnalysisSystemPrompt();
24
- const initialAnalysisPrompt = context.analysisPrompt;
25
- const maxIterations = options.maxVerificationIterations || 10;
26
- let currentIteration = 0;
27
- let currentPrompt = initialAnalysisPrompt;
28
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
29
- let structuredAnalysisResult = null;
30
- let verificationResult = null;
31
- if (verbose) {
32
- logInfo('Starting Claude Code query with pre-fetched information...');
33
- }
34
- // Iterative improvement loop: analysis → save draft → verification → update ready or delete → re-analysis
35
- let currentDraftUserStoryIds = [];
36
- let currentDraftTestCaseIds = [];
37
- while (currentIteration < maxIterations) {
38
- currentIteration++;
39
- if (verbose && currentIteration > 1) {
40
- logInfo(`\n🔄 Iteration ${currentIteration}/${maxIterations}: Improving analysis based on verification feedback...`);
41
- }
42
- // Log iteration start (for iterations after the first)
43
- if (currentIteration > 1) {
44
- await logFeaturePhaseEvent({
45
- featureId,
46
- eventType: 'phase_started',
47
- phase: 'feature_analysis',
48
- result: 'info',
49
- metadata: {
50
- iteration: currentIteration,
51
- max_iterations: maxIterations,
52
- re_analysis: true,
53
- timestamp: new Date().toISOString(),
54
- },
55
- }, verbose);
56
- }
57
- // Execute analysis query
58
- structuredAnalysisResult = await executeAnalysisQuery(currentPrompt, systemPrompt, config, verbose);
59
- if (!structuredAnalysisResult) {
60
- break;
61
- }
62
- await logFeaturePhaseEvent({
63
- featureId,
64
- eventType: 'phase_completed',
65
- phase: 'feature_analysis',
66
- result: 'success',
67
- metadata: {
68
- iteration: currentIteration,
69
- max_iterations: maxIterations,
70
- analysis_step: 'completed',
71
- user_stories_count: structuredAnalysisResult.created_user_stories?.length || 0,
72
- test_cases_count: structuredAnalysisResult.created_test_cases?.length || 0,
73
- deleted_user_stories_count: structuredAnalysisResult.deleted_user_story_ids?.length || 0,
74
- deleted_test_cases_count: structuredAnalysisResult.deleted_test_case_ids?.length || 0,
75
- timestamp: new Date().toISOString(),
76
- },
77
- }, verbose);
78
- // Handle deletions identified by AI
79
- if (structuredAnalysisResult.deleted_user_story_ids?.length > 0 ||
80
- structuredAnalysisResult.deleted_test_case_ids?.length > 0) {
81
- if (verbose) {
82
- logInfo('🗑️ Processing deletions identified by AI during analysis...');
83
- }
84
- await deleteSpecificArtifacts(featureId, structuredAnalysisResult.deleted_user_story_ids || [], structuredAnalysisResult.deleted_test_case_ids || [], structuredAnalysisResult.deletion_reasons || {}, verbose);
85
- }
86
- // Save artifacts as draft and get their IDs
87
- const { userStoryIds, testCaseIds } = await saveAnalysisArtifactsAsDraft(featureId, structuredAnalysisResult.created_user_stories || [], structuredAnalysisResult.created_test_cases || [], verbose);
88
- currentDraftUserStoryIds = userStoryIds;
89
- currentDraftTestCaseIds = testCaseIds;
90
- // Coaching loop between execute and verify
91
- await runPhaseCoaching({
92
- featureId,
93
- phase: 'feature_analysis',
94
- phaseResult: structuredAnalysisResult,
95
- checklistItems: extractChecklistItems(checklistContext),
96
- onImproved: async (_sessionId, responseText) => {
97
- const parsed = parseAnalysisResult(responseText);
98
- if (parsed.analysis) {
99
- // Delete old drafts and save improved ones
100
- await deleteArtifacts(currentDraftUserStoryIds, currentDraftTestCaseIds, verbose);
101
- const { userStoryIds: newUserStoryIds, testCaseIds: newTestCaseIds, } = await saveAnalysisArtifactsAsDraft(featureId, parsed.analysis.created_user_stories || [], parsed.analysis.created_test_cases || [], verbose);
102
- currentDraftUserStoryIds = newUserStoryIds;
103
- currentDraftTestCaseIds = newTestCaseIds;
104
- structuredAnalysisResult = parsed.analysis;
105
- }
106
- else {
107
- logWarning(`Coaching improvement response could not be parsed for feature-analysis. Keeping previous drafts. ${parsed.error || ''}`);
108
- }
109
- },
110
- verbose,
111
- });
112
- // Perform verification cycle
113
- const verificationCycle = await performVerificationCycle({
114
- structuredAnalysisResult,
115
- checklistContext: checklistContext || null,
116
- context: context.featureContext,
117
- config,
118
- currentIteration,
119
- maxIterations,
120
- featureId,
121
- verbose,
122
- });
123
- ({ verificationResult } = verificationCycle);
124
- // If verification passed, update ALL remaining draft artifacts to pending_approval and exit
125
- if (verificationCycle.passed) {
126
- if (verbose) {
127
- logInfo('✅ Verification passed! Updating all draft artifacts to pending_approval status...');
128
- }
129
- const allDrafts = await getAllDraftArtifactIds(featureId, verbose);
130
- await updateArtifactsToReady(allDrafts.userStoryIds, allDrafts.testCaseIds, verbose);
131
- break;
132
- }
133
- // Verification failed
134
- if (currentIteration < maxIterations && verificationCycle.nextPrompt) {
135
- // We have more iterations - delete draft artifacts and retry
136
- if (verbose) {
137
- logInfo('🗑️ Deleting draft artifacts for re-analysis...');
138
- }
139
- await deleteArtifacts(currentDraftUserStoryIds, currentDraftTestCaseIds, verbose);
140
- // Continue with improvement prompt
141
- currentPrompt = verificationCycle.nextPrompt;
142
- }
143
- else {
144
- // Max iterations reached or no next prompt - exit loop
145
- // Draft artifacts remain in database for manual review
146
- if (verbose) {
147
- logInfo('⚠️ Max iterations reached. Draft artifacts kept for manual review.');
148
- }
149
- break;
150
- }
151
- }
152
- // Handle results
153
- if (!structuredAnalysisResult) {
154
- throw new Error('No analysis results received');
155
- }
156
- // If no checklist was used, update all draft artifacts to pending_approval now
157
- if (!checklistContext ||
158
- checklistContext.checklists.length === 0 ||
159
- !verificationResult) {
160
- const allDrafts = await getAllDraftArtifactIds(featureId, verbose);
161
- if (allDrafts.userStoryIds.length > 0 ||
162
- allDrafts.testCaseIds.length > 0) {
163
- if (verbose) {
164
- logInfo('✅ No checklist verification needed. Updating all draft artifacts to pending_approval status...');
165
- }
166
- await updateArtifactsToReady(allDrafts.userStoryIds, allDrafts.testCaseIds, verbose);
167
- }
168
- }
169
- // Check if verification failed after all iterations
170
- // Note: Artifacts are already saved as draft in the database
171
- // If verification failed, they remain as draft for manual review
172
- if (verificationResult &&
173
- verificationResult.rejected_count > 0 &&
174
- checklistContext &&
175
- checklistContext.checklists.length > 0) {
176
- logError(`❌ Final result: Checklist verification FAILED after ${currentIteration} iterations`);
177
- logError(` Draft artifacts (${currentDraftUserStoryIds.length} user stories, ${currentDraftTestCaseIds.length} test cases) kept for manual review`);
178
- throw new Error(`Checklist verification failed after ${currentIteration} iterations`);
179
- }
180
- // Return success result
181
- // Note: Artifacts have already been saved and updated to 'pending_approval' status (if verification passed)
182
- // or remain as draft (if verification failed)
183
- return buildAnalysisResult(featureId, context.featureContext, structuredAnalysisResult, currentIteration);
184
- }
185
- catch (error) {
186
- logError(`Feature analysis failed: ${error instanceof Error ? error.message : String(error)}`);
187
- return {
188
- featureId,
189
- productInfo: null,
190
- featureInfo: null,
191
- existingUserStories: [],
192
- existingTestCases: [],
193
- createdUserStories: [],
194
- createdTestCases: [],
195
- summary: `Analysis failed: ${error instanceof Error ? error.message : String(error)}`,
196
- status: 'error',
197
- };
198
- }
199
- };
@@ -1,40 +0,0 @@
1
- import { type FeatureAnalysisResult } from '../../types/index.js';
2
- import { type FeatureAnalysisContext } from './context.js';
3
- /**
4
- * Reset ready user stories and test cases to draft for re-analysis
5
- */
6
- export declare function resetReadyArtifactsToDraft(featureId: string, verbose?: boolean): Promise<{
7
- resetUserStories: number;
8
- resetTestCases: number;
9
- }>;
10
- /**
11
- * Get all draft artifact IDs for a feature
12
- */
13
- export declare function getAllDraftArtifactIds(featureId: string, verbose?: boolean): Promise<{
14
- userStoryIds: string[];
15
- testCaseIds: string[];
16
- }>;
17
- /**
18
- * Delete artifacts after verification failure
19
- */
20
- export declare function deleteArtifacts(userStoryIds: string[], testCaseIds: string[], verbose?: boolean): Promise<void>;
21
- /**
22
- * Delete specific artifacts identified by AI during analysis
23
- * Only deletes artifacts with status 'draft'
24
- */
25
- export declare function deleteSpecificArtifacts(featureId: string, deletedUserStoryIds: string[], deletedTestCaseIds: string[], deletionReasons: Record<string, string>, verbose?: boolean): Promise<void>;
26
- /**
27
- * Update artifacts to pending_approval status after successful verification
28
- */
29
- export declare function updateArtifactsToReady(userStoryIds: string[], testCaseIds: string[], verbose?: boolean): Promise<void>;
30
- /**
31
- * Save analysis artifacts as draft and return their IDs
32
- */
33
- export declare function saveAnalysisArtifactsAsDraft(featureId: string, createdUserStories: Record<string, unknown>[], createdTestCases: Record<string, unknown>[], verbose?: boolean): Promise<{
34
- userStoryIds: string[];
35
- testCaseIds: string[];
36
- }>;
37
- /**
38
- * Build the final analysis result object
39
- */
40
- export declare function buildAnalysisResult(featureId: string, context: FeatureAnalysisContext, structuredAnalysisResult: any, currentIteration: number): FeatureAnalysisResult;