edsger 0.2.1 → 0.2.2

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 (178) hide show
  1. package/dist/api/features/feature-utils.d.ts +13 -0
  2. package/dist/api/features/feature-utils.js +46 -0
  3. package/dist/api/features/get-feature.d.ts +5 -0
  4. package/dist/api/features/get-feature.js +19 -0
  5. package/dist/api/features/index.d.ts +7 -0
  6. package/dist/api/features/index.js +9 -0
  7. package/dist/api/features/status-updater.d.ts +27 -0
  8. package/dist/api/features/status-updater.js +64 -0
  9. package/dist/api/features/test-cases.d.ts +21 -0
  10. package/dist/api/features/test-cases.js +63 -0
  11. package/dist/api/features/update-feature.d.ts +13 -0
  12. package/dist/api/features/update-feature.js +31 -0
  13. package/dist/api/features/user-stories.d.ts +21 -0
  14. package/dist/api/features/user-stories.js +63 -0
  15. package/dist/api/features.d.ts +100 -0
  16. package/dist/api/features.js +219 -0
  17. package/dist/api/mcp-client.d.ts +18 -0
  18. package/dist/api/mcp-client.js +58 -0
  19. package/dist/api/products.d.ts +10 -0
  20. package/dist/api/products.js +22 -0
  21. package/dist/api/test-reports.d.ts +9 -0
  22. package/dist/api/test-reports.js +25 -0
  23. package/dist/cli/commands/code-implementation-command.d.ts +2 -0
  24. package/dist/cli/commands/code-implementation-command.js +36 -0
  25. package/dist/cli/commands/code-review-command.d.ts +2 -0
  26. package/dist/cli/commands/code-review-command.js +39 -0
  27. package/dist/cli/commands/feature-analysis-command.d.ts +2 -0
  28. package/dist/cli/commands/feature-analysis-command.js +36 -0
  29. package/dist/cli/commands/functional-testing-command.d.ts +2 -0
  30. package/dist/cli/commands/functional-testing-command.js +36 -0
  31. package/dist/cli/commands/technical-design-command.d.ts +2 -0
  32. package/dist/cli/commands/technical-design-command.js +36 -0
  33. package/dist/cli/commands/workflow-command.d.ts +2 -0
  34. package/dist/cli/commands/workflow-command.js +34 -0
  35. package/dist/cli/formatters/code-implementation-formatter.d.ts +9 -0
  36. package/dist/cli/formatters/code-implementation-formatter.js +27 -0
  37. package/dist/cli/formatters/feature-analysis-formatter.d.ts +2 -0
  38. package/dist/cli/formatters/feature-analysis-formatter.js +27 -0
  39. package/dist/cli/formatters/functional-testing-formatter.d.ts +15 -0
  40. package/dist/cli/formatters/functional-testing-formatter.js +37 -0
  41. package/dist/cli/formatters/technical-design-formatter.d.ts +7 -0
  42. package/dist/cli/formatters/technical-design-formatter.js +30 -0
  43. package/dist/cli/index.d.ts +3 -0
  44. package/dist/cli/index.js +99 -0
  45. package/dist/cli/utils/validation.d.ts +25 -0
  46. package/dist/cli/utils/validation.js +58 -0
  47. package/dist/cli/utils/workflow-utils.d.ts +21 -0
  48. package/dist/cli/utils/workflow-utils.js +47 -0
  49. package/dist/cli.d.ts +1 -1
  50. package/dist/cli.js +11 -466
  51. package/dist/config.d.ts +1 -1
  52. package/dist/index.d.ts +3 -3
  53. package/dist/index.js +2 -2
  54. package/dist/{bug-fixing → phases/bug-fixing}/analyzer.d.ts +1 -1
  55. package/dist/{bug-fixing → phases/bug-fixing}/analyzer.js +1 -1
  56. package/dist/{bug-fixing → phases/bug-fixing}/context-fetcher.d.ts +4 -22
  57. package/dist/{bug-fixing → phases/bug-fixing}/context-fetcher.js +14 -58
  58. package/dist/{bug-fixing → phases/bug-fixing}/mcp-server.js +1 -30
  59. package/dist/phases/code-implementation/analyzer.d.ts +33 -0
  60. package/dist/{code-implementation → phases/code-implementation}/analyzer.js +174 -15
  61. package/dist/phases/code-implementation/context-fetcher.d.ts +17 -0
  62. package/dist/phases/code-implementation/context-fetcher.js +86 -0
  63. package/dist/{code-implementation → phases/code-implementation}/mcp-server.js +1 -30
  64. package/dist/{code-review → phases/code-review}/reviewer.d.ts +1 -1
  65. package/dist/{feature-analysis → phases/feature-analysis}/analyzer.d.ts +3 -2
  66. package/dist/{feature-analysis → phases/feature-analysis}/analyzer.js +29 -127
  67. package/dist/phases/feature-analysis/context-fetcher.d.ts +18 -0
  68. package/dist/phases/feature-analysis/context-fetcher.js +86 -0
  69. package/dist/{feature-analysis → phases/feature-analysis}/http-fallback.js +1 -1
  70. package/dist/{feature-analysis → phases/feature-analysis}/mcp-server.js +1 -24
  71. package/dist/{functional-testing → phases/functional-testing}/analyzer.d.ts +17 -2
  72. package/dist/{functional-testing → phases/functional-testing}/analyzer.js +225 -31
  73. package/dist/phases/functional-testing/context-fetcher.d.ts +16 -0
  74. package/dist/phases/functional-testing/context-fetcher.js +81 -0
  75. package/dist/{functional-testing → phases/functional-testing}/http-fallback.js +1 -1
  76. package/dist/{functional-testing → phases/functional-testing}/index.d.ts +1 -1
  77. package/dist/{functional-testing → phases/functional-testing}/index.js +1 -1
  78. package/dist/{functional-testing → phases/functional-testing}/mcp-server.js +1 -30
  79. package/dist/{functional-testing → phases/functional-testing}/test-report-creator.js +1 -1
  80. package/dist/phases/functional-testing/test-retry-handler.d.ts +16 -0
  81. package/dist/phases/functional-testing/test-retry-handler.js +75 -0
  82. package/dist/{pull-request → phases/pull-request}/creator.js +47 -6
  83. package/dist/phases/pull-request/handler.d.ts +16 -0
  84. package/dist/phases/pull-request/handler.js +60 -0
  85. package/dist/{technical-design → phases/technical-design}/analyzer.d.ts +7 -2
  86. package/dist/phases/technical-design/analyzer.js +418 -0
  87. package/dist/phases/technical-design/context-fetcher.d.ts +12 -0
  88. package/dist/phases/technical-design/context-fetcher.js +39 -0
  89. package/dist/{technical-design → phases/technical-design}/http-fallback.js +1 -1
  90. package/dist/{technical-design → phases/technical-design}/mcp-server.js +1 -30
  91. package/dist/prompts/bug-fixing.d.ts +2 -0
  92. package/dist/prompts/bug-fixing.js +63 -0
  93. package/dist/prompts/code-implementation.d.ts +3 -0
  94. package/dist/prompts/code-implementation.js +132 -0
  95. package/dist/prompts/feature-analysis.d.ts +3 -0
  96. package/dist/prompts/feature-analysis.js +149 -0
  97. package/dist/prompts/formatters.d.ts +29 -0
  98. package/dist/prompts/formatters.js +139 -0
  99. package/dist/prompts/functional-testing.d.ts +3 -0
  100. package/dist/prompts/functional-testing.js +126 -0
  101. package/dist/prompts/index.d.ts +6 -0
  102. package/dist/prompts/index.js +7 -0
  103. package/dist/prompts/technical-design.d.ts +3 -0
  104. package/dist/prompts/technical-design.js +130 -0
  105. package/dist/services/checklist.d.ts +99 -0
  106. package/dist/services/checklist.js +337 -0
  107. package/dist/types/features.d.ts +29 -0
  108. package/dist/types/features.js +1 -0
  109. package/dist/types/index.d.ts +112 -0
  110. package/dist/types/index.js +1 -0
  111. package/dist/types/pipeline.d.ts +25 -0
  112. package/dist/types/pipeline.js +4 -0
  113. package/dist/utils/logger.d.ts +19 -0
  114. package/dist/utils/logger.js +52 -0
  115. package/dist/utils/pipeline-logger.d.ts +8 -0
  116. package/dist/utils/pipeline-logger.js +35 -0
  117. package/dist/workflow-runner/config/phase-configs.d.ts +5 -0
  118. package/dist/workflow-runner/config/phase-configs.js +34 -0
  119. package/dist/workflow-runner/config/stage-configs.d.ts +5 -0
  120. package/dist/workflow-runner/config/stage-configs.js +34 -0
  121. package/dist/workflow-runner/core/feature-filter.d.ts +16 -0
  122. package/dist/workflow-runner/core/feature-filter.js +46 -0
  123. package/dist/workflow-runner/core/feature-filter.test.d.ts +4 -0
  124. package/dist/workflow-runner/core/feature-filter.test.js +127 -0
  125. package/dist/workflow-runner/core/index.d.ts +8 -0
  126. package/dist/workflow-runner/core/index.js +12 -0
  127. package/dist/workflow-runner/core/pipeline-evaluator.d.ts +24 -0
  128. package/dist/workflow-runner/core/pipeline-evaluator.js +32 -0
  129. package/dist/workflow-runner/core/state-manager.d.ts +24 -0
  130. package/dist/workflow-runner/core/state-manager.js +42 -0
  131. package/dist/workflow-runner/core/workflow-logger.d.ts +20 -0
  132. package/dist/workflow-runner/core/workflow-logger.js +65 -0
  133. package/dist/workflow-runner/executors/phase-executor.d.ts +8 -0
  134. package/dist/workflow-runner/executors/phase-executor.js +183 -0
  135. package/dist/workflow-runner/executors/stage-executor.d.ts +8 -0
  136. package/dist/workflow-runner/executors/stage-executor.js +49 -0
  137. package/dist/workflow-runner/feature-service.d.ts +17 -0
  138. package/dist/workflow-runner/feature-service.js +60 -0
  139. package/dist/workflow-runner/feature-workflow-runner.d.ts +26 -0
  140. package/dist/workflow-runner/feature-workflow-runner.js +113 -0
  141. package/dist/workflow-runner/index.d.ts +0 -1
  142. package/dist/workflow-runner/index.js +0 -1
  143. package/dist/workflow-runner/pipeline-runner.d.ts +9 -19
  144. package/dist/workflow-runner/pipeline-runner.js +247 -256
  145. package/dist/workflow-runner/pipeline.d.ts +18 -0
  146. package/dist/workflow-runner/pipeline.js +197 -0
  147. package/dist/workflow-runner/processor.d.ts +40 -0
  148. package/dist/workflow-runner/processor.js +191 -0
  149. package/dist/workflow-runner/types.d.ts +48 -0
  150. package/dist/workflow-runner/types.js +4 -0
  151. package/dist/workflow-runner/workflow-processor.d.ts +6 -23
  152. package/dist/workflow-runner/workflow-processor.js +38 -100
  153. package/package.json +1 -1
  154. package/dist/code-implementation/analyzer.d.ts +0 -19
  155. package/dist/code-implementation/context-fetcher.d.ts +0 -38
  156. package/dist/code-implementation/context-fetcher.js +0 -147
  157. package/dist/feature-analysis/context-fetcher.d.ts +0 -54
  158. package/dist/feature-analysis/context-fetcher.js +0 -193
  159. package/dist/functional-testing/context-fetcher.d.ts +0 -47
  160. package/dist/functional-testing/context-fetcher.js +0 -192
  161. package/dist/technical-design/analyzer.js +0 -338
  162. package/dist/technical-design/context-fetcher.d.ts +0 -42
  163. package/dist/technical-design/context-fetcher.js +0 -170
  164. /package/dist/{bug-fixing → phases/bug-fixing}/index.d.ts +0 -0
  165. /package/dist/{bug-fixing → phases/bug-fixing}/index.js +0 -0
  166. /package/dist/{bug-fixing → phases/bug-fixing}/mcp-server.d.ts +0 -0
  167. /package/dist/{code-implementation → phases/code-implementation}/mcp-server.d.ts +0 -0
  168. /package/dist/{code-review → phases/code-review}/reviewer.js +0 -0
  169. /package/dist/{feature-analysis → phases/feature-analysis}/http-fallback.d.ts +0 -0
  170. /package/dist/{feature-analysis → phases/feature-analysis}/index.d.ts +0 -0
  171. /package/dist/{feature-analysis → phases/feature-analysis}/index.js +0 -0
  172. /package/dist/{feature-analysis → phases/feature-analysis}/mcp-server.d.ts +0 -0
  173. /package/dist/{functional-testing → phases/functional-testing}/http-fallback.d.ts +0 -0
  174. /package/dist/{functional-testing → phases/functional-testing}/mcp-server.d.ts +0 -0
  175. /package/dist/{functional-testing → phases/functional-testing}/test-report-creator.d.ts +0 -0
  176. /package/dist/{pull-request → phases/pull-request}/creator.d.ts +0 -0
  177. /package/dist/{technical-design → phases/technical-design}/http-fallback.d.ts +0 -0
  178. /package/dist/{technical-design → phases/technical-design}/mcp-server.d.ts +0 -0
@@ -91,7 +91,7 @@ export async function createPullRequest(config, feature) {
91
91
  const octokit = new Octokit({
92
92
  auth: githubToken,
93
93
  });
94
- // Get current branch
94
+ // Get current branch (should be dev/feature-id)
95
95
  const currentBranch = getCurrentBranch();
96
96
  if (verbose) {
97
97
  console.log(`🔍 Current branch: ${currentBranch}`);
@@ -103,22 +103,63 @@ export async function createPullRequest(config, feature) {
103
103
  error: `Cannot create PR: already on ${baseBranch} branch`,
104
104
  };
105
105
  }
106
- // Push current branch to remote
107
- pushBranch(currentBranch, verbose);
106
+ // Extract feature ID from current branch (dev/feature-id)
107
+ let targetBranch = baseBranch;
108
+ if (currentBranch.startsWith('dev/')) {
109
+ const featureId = currentBranch.replace('dev/', '');
110
+ targetBranch = `feat/${featureId}`;
111
+ // Create the feat/ branch from current dev/ branch
112
+ if (verbose) {
113
+ console.log(`📝 Creating target branch: ${targetBranch} from ${currentBranch}`);
114
+ }
115
+ try {
116
+ // Push current branch to remote first
117
+ pushBranch(currentBranch, verbose);
118
+ // Get the current branch SHA
119
+ const { data: branchData } = await octokit.repos.getBranch({
120
+ owner,
121
+ repo,
122
+ branch: currentBranch,
123
+ });
124
+ // Create the feat/ branch
125
+ await octokit.git.createRef({
126
+ owner,
127
+ repo,
128
+ ref: `refs/heads/${targetBranch}`,
129
+ sha: branchData.commit.sha,
130
+ });
131
+ if (verbose) {
132
+ console.log(`✅ Created target branch: ${targetBranch}`);
133
+ }
134
+ }
135
+ catch (error) {
136
+ // If branch already exists, that's fine
137
+ if (!error.message?.includes('Reference already exists')) {
138
+ throw error;
139
+ }
140
+ if (verbose) {
141
+ console.log(`ℹ️ Target branch ${targetBranch} already exists`);
142
+ }
143
+ }
144
+ }
145
+ else {
146
+ // If not a dev/ branch, push it normally
147
+ pushBranch(currentBranch, verbose);
148
+ }
108
149
  // Generate PR title and body
109
150
  const title = generatePRTitle(feature.name);
110
151
  const body = generatePRBody(feature);
111
152
  if (verbose) {
112
- console.log(`📝 Creating pull request: ${title}`);
153
+ console.log(`📝 Creating pull request: ${title} from ${currentBranch} to ${targetBranch}`);
113
154
  }
114
- // Create the pull request
155
+ // Create the pull request from dev/ to feat/
115
156
  const { data: pullRequest } = await octokit.pulls.create({
116
157
  owner,
117
158
  repo,
118
159
  title,
119
160
  body,
120
161
  head: currentBranch,
121
- base: baseBranch,
162
+ base: targetBranch,
122
163
  draft: false,
123
164
  });
124
165
  if (verbose) {
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Pull request creation handler for pipeline execution
3
+ */
4
+ import { PipelineResult } from '../../types/pipeline.js';
5
+ export interface PullRequestHandlerOptions {
6
+ featureId: string;
7
+ mcpServerUrl: string;
8
+ mcpToken: string;
9
+ results: readonly PipelineResult[];
10
+ testingResult: PipelineResult;
11
+ verbose?: boolean;
12
+ }
13
+ /**
14
+ * Create pull request for successful feature implementation
15
+ */
16
+ export declare function handlePullRequestCreation({ featureId, mcpServerUrl, mcpToken, results, testingResult, verbose, }: PullRequestHandlerOptions): Promise<void>;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Pull request creation handler for pipeline execution
3
+ */
4
+ import { getFeature } from '../../api/features/index.js';
5
+ import { createPullRequest, updateFeatureWithPullRequest } from './creator.js';
6
+ /**
7
+ * Create pull request for successful feature implementation
8
+ */
9
+ export async function handlePullRequestCreation({ featureId, mcpServerUrl, mcpToken, results, testingResult, verbose, }) {
10
+ if (verbose) {
11
+ console.log('🔄 Creating pull request for successful feature...');
12
+ }
13
+ // Get feature details for PR creation
14
+ const feature = await getFeature(mcpServerUrl, mcpToken, featureId, verbose);
15
+ if (!feature) {
16
+ if (verbose) {
17
+ console.log('⚠️ Could not fetch feature details for pull request creation');
18
+ }
19
+ return;
20
+ }
21
+ // Get GitHub configuration from environment
22
+ const githubToken = process.env.GITHUB_TOKEN || process.env.GITHUB_ACCESS_TOKEN;
23
+ const githubOwner = process.env.GITHUB_OWNER;
24
+ const githubRepo = process.env.GITHUB_REPO;
25
+ if (!githubToken || !githubOwner || !githubRepo) {
26
+ if (verbose) {
27
+ console.log('⚠️ GitHub configuration not found. Skipping pull request creation.');
28
+ console.log(' Set GITHUB_TOKEN, GITHUB_OWNER, and GITHUB_REPO environment variables.');
29
+ }
30
+ return;
31
+ }
32
+ const prResult = await createPullRequest({
33
+ githubToken,
34
+ owner: githubOwner,
35
+ repo: githubRepo,
36
+ baseBranch: 'main',
37
+ verbose,
38
+ }, {
39
+ id: feature.id,
40
+ name: feature.name,
41
+ description: feature.description || '',
42
+ productId: feature.product_id,
43
+ // Get technical design and test result from previous phases
44
+ technicalDesign: results.find((r) => r.phase === 'technical-design')?.data?.technicalDesign,
45
+ testResult: testingResult.data?.testResult,
46
+ testReportUrl: testingResult.data?.testReport?.testReportUrl,
47
+ });
48
+ if (prResult.success && prResult.pullRequestUrl) {
49
+ // Update feature with PR URL and status
50
+ await updateFeatureWithPullRequest(mcpServerUrl, mcpToken, featureId, prResult.pullRequestUrl, verbose);
51
+ if (verbose) {
52
+ console.log(`✅ Pull request created and feature updated to ready_for_review`);
53
+ }
54
+ }
55
+ else {
56
+ if (verbose) {
57
+ console.log(`⚠️ Pull request creation failed: ${prResult.error}`);
58
+ }
59
+ }
60
+ }
@@ -1,4 +1,5 @@
1
- import { EdsgerConfig } from '../types.js';
1
+ import { EdsgerConfig } from '../../types/index.js';
2
+ import { ChecklistPhaseContext } from '../../services/checklist.js';
2
3
  export interface TechnicalDesignOptions {
3
4
  featureId: string;
4
5
  mcpServerUrl: string;
@@ -11,6 +12,10 @@ export interface TechnicalDesignResult {
11
12
  status: 'success' | 'error';
12
13
  summary: string;
13
14
  savedViaHttp?: boolean;
15
+ data?: {
16
+ checklist_item_results?: any[];
17
+ [key: string]: any;
18
+ };
14
19
  }
15
- export declare const generateTechnicalDesign: (options: TechnicalDesignOptions, config: EdsgerConfig) => Promise<TechnicalDesignResult>;
20
+ export declare const generateTechnicalDesign: (options: TechnicalDesignOptions, config: EdsgerConfig, checklistContext?: ChecklistPhaseContext | null) => Promise<TechnicalDesignResult>;
16
21
  export declare const checkTechnicalDesignRequirements: () => Promise<boolean>;
@@ -0,0 +1,418 @@
1
+ import { query } from '@anthropic-ai/claude-code';
2
+ import { logInfo, logError } from '../../utils/logger.js';
3
+ import { saveTechnicalDesignViaHttp } from './http-fallback.js';
4
+ import { fetchTechnicalDesignContext, } from './context-fetcher.js';
5
+ import { updateTechnicalDesign } from '../../api/features/index.js';
6
+ import { formatTechnicalDesignContext } from '../../prompts/formatters.js';
7
+ import { createTechnicalDesignSystemPrompt, createTechnicalDesignPromptWithContext, } from '../../prompts/technical-design.js';
8
+ import { formatChecklistsForContext, } from '../../services/checklist.js';
9
+ function userMessage(content) {
10
+ return {
11
+ type: 'user',
12
+ message: { role: 'user', content: content },
13
+ };
14
+ }
15
+ async function* prompt(analysisPrompt) {
16
+ yield userMessage(analysisPrompt);
17
+ await new Promise((res) => setTimeout(res, 10000));
18
+ }
19
+ export const generateTechnicalDesign = async (options, config, checklistContext) => {
20
+ const { featureId, mcpServerUrl, mcpToken, verbose } = options;
21
+ if (verbose) {
22
+ logInfo(`Starting technical design generation for feature ID: ${featureId}`);
23
+ logInfo(`Using MCP server: ${mcpServerUrl}`);
24
+ }
25
+ try {
26
+ // Fetch all required context information via MCP endpoints
27
+ if (verbose) {
28
+ logInfo('Fetching technical design context via MCP endpoints...');
29
+ }
30
+ const context = await fetchTechnicalDesignContext(mcpServerUrl, mcpToken, featureId, verbose);
31
+ const systemPrompt = createTechnicalDesignSystemPrompt(config, mcpServerUrl, mcpToken, featureId);
32
+ const contextInfo = formatTechnicalDesignContext(context);
33
+ // Add checklist context to the design prompt
34
+ let finalContextInfo = contextInfo;
35
+ if (checklistContext && checklistContext.checklists.length > 0) {
36
+ const checklistInfo = formatChecklistsForContext(checklistContext);
37
+ finalContextInfo = contextInfo + '\n\n' + checklistInfo;
38
+ if (verbose) {
39
+ logInfo(`Added ${checklistContext.checklists.length} checklists to design context`);
40
+ }
41
+ }
42
+ const designPrompt = createTechnicalDesignPromptWithContext(featureId, finalContextInfo);
43
+ let lastAssistantResponse = '';
44
+ let structuredDesignResult = null;
45
+ if (verbose) {
46
+ logInfo('Starting Claude Code query with pre-fetched information...');
47
+ }
48
+ // Use Claude Code SDK without MCP servers - all info is pre-fetched
49
+ for await (const message of query({
50
+ prompt: prompt(designPrompt),
51
+ options: {
52
+ appendSystemPrompt: systemPrompt,
53
+ model: config.claude.model || 'sonnet',
54
+ maxTurns: 1000,
55
+ permissionMode: 'bypassPermissions',
56
+ },
57
+ })) {
58
+ if (verbose) {
59
+ logInfo(`Received message type: ${message.type}`);
60
+ }
61
+ // Stream the technical design generation process
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
+ if (verbose) {
67
+ console.log(`\n🤖 ${content.text}`);
68
+ }
69
+ }
70
+ else if (content.type === 'tool_use') {
71
+ if (verbose) {
72
+ console.log(`\n🔧 ${content.name}: ${content.input.description || 'Running...'}`);
73
+ }
74
+ }
75
+ }
76
+ }
77
+ if (message.type === 'result') {
78
+ if (message.subtype === 'success') {
79
+ logInfo('\n🎨 Technical design generation completed, parsing results...');
80
+ try {
81
+ // Try to extract JSON from markdown code block or parse directly
82
+ const responseText = message.result || lastAssistantResponse;
83
+ let jsonResult = null;
84
+ // DEBUG: Log response text details
85
+ console.log('=== DEBUG: Response Text Analysis ===');
86
+ console.log('message.result exists:', !!message.result);
87
+ console.log('lastAssistantResponse exists:', !!lastAssistantResponse);
88
+ console.log('responseText length:', responseText.length);
89
+ console.log('responseText first 200 chars:', JSON.stringify(responseText.substring(0, 200)));
90
+ console.log('responseText last 200 chars:', JSON.stringify(responseText.substring(responseText.length - 200)));
91
+ console.log('Contains ```json:', responseText.includes('```json'));
92
+ console.log('Contains technical_design_result:', responseText.includes('"technical_design_result"'));
93
+ // First try to extract JSON from markdown code block
94
+ const jsonBlockMatch = responseText.match(/```json\s*\n([\s\S]*?)\n\s*```/);
95
+ console.log('=== DEBUG: Markdown Block Match ===');
96
+ console.log('jsonBlockMatch found:', !!jsonBlockMatch);
97
+ if (jsonBlockMatch) {
98
+ console.log('Matched JSON length:', jsonBlockMatch[1].length);
99
+ console.log('Matched JSON first 100 chars:', JSON.stringify(jsonBlockMatch[1].substring(0, 100)));
100
+ jsonResult = JSON.parse(jsonBlockMatch[1]);
101
+ }
102
+ else {
103
+ console.log('=== DEBUG: JSON Object Search ===');
104
+ // Try to find JSON object containing "technical_design_result"
105
+ const lines = responseText.split('\n');
106
+ let jsonStartIndex = -1;
107
+ console.log('Total lines:', lines.length);
108
+ console.log('Lines preview:', lines
109
+ .slice(0, 10)
110
+ .map((line, i) => `${i}: ${JSON.stringify(line.substring(0, 50))}`));
111
+ // Find the line that contains the start of a JSON object with "technical_design_result"
112
+ for (let i = 0; i < lines.length; i++) {
113
+ const line = lines[i].trim();
114
+ if (line.startsWith('{') &&
115
+ responseText.includes('"technical_design_result"')) {
116
+ console.log(`Found { at line ${i}: ${JSON.stringify(line)}`);
117
+ const fromThisLine = lines.slice(i).join('\n');
118
+ if (fromThisLine.includes('"technical_design_result"')) {
119
+ jsonStartIndex = responseText.indexOf(lines[i]);
120
+ console.log(`JSON start found at line ${i}, responseText index: ${jsonStartIndex}`);
121
+ break;
122
+ }
123
+ }
124
+ }
125
+ if (jsonStartIndex !== -1) {
126
+ // Find the complete JSON object starting from this position
127
+ let braceCount = 0;
128
+ let endIndex = jsonStartIndex;
129
+ let inString = false;
130
+ let escapeNext = false;
131
+ for (let i = jsonStartIndex; i < responseText.length; i++) {
132
+ const char = responseText[i];
133
+ if (escapeNext) {
134
+ escapeNext = false;
135
+ continue;
136
+ }
137
+ if (char === '\\' && inString) {
138
+ escapeNext = true;
139
+ continue;
140
+ }
141
+ if (char === '"') {
142
+ inString = !inString;
143
+ continue;
144
+ }
145
+ if (!inString) {
146
+ if (char === '{') {
147
+ braceCount++;
148
+ }
149
+ else if (char === '}') {
150
+ braceCount--;
151
+ if (braceCount === 0) {
152
+ endIndex = i;
153
+ break;
154
+ }
155
+ }
156
+ }
157
+ }
158
+ const jsonStr = responseText.substring(jsonStartIndex, endIndex + 1);
159
+ console.log('=== DEBUG: JSON Extraction ===');
160
+ console.log('JSON start index:', jsonStartIndex);
161
+ console.log('JSON end index:', endIndex);
162
+ console.log('Extracted JSON length:', jsonStr.length);
163
+ console.log('Extracted JSON first 100 chars:', JSON.stringify(jsonStr.substring(0, 100)));
164
+ console.log('Extracted JSON last 100 chars:', JSON.stringify(jsonStr.substring(jsonStr.length - 100)));
165
+ try {
166
+ jsonResult = JSON.parse(jsonStr);
167
+ console.log('JSON parsing succeeded!');
168
+ }
169
+ catch (parseError) {
170
+ console.log('Failed to parse extracted JSON:', parseError);
171
+ console.log('Extracted JSON string:', jsonStr.substring(0, 200) + '...');
172
+ console.log('=== DEBUG: Fallback to full response ===');
173
+ console.log('Attempting to parse entire responseText as JSON');
174
+ // Try to parse the entire response as JSON as fallback
175
+ jsonResult = JSON.parse(responseText);
176
+ }
177
+ }
178
+ else {
179
+ console.log('=== DEBUG: No JSON start found ===');
180
+ console.log('Falling back to parse entire responseText as JSON');
181
+ // Try to parse the entire response as JSON
182
+ jsonResult = JSON.parse(responseText);
183
+ }
184
+ }
185
+ console.log('=== DEBUG: Final Result Processing ===');
186
+ console.log('jsonResult exists:', !!jsonResult);
187
+ console.log('jsonResult has technical_design_result:', !!(jsonResult && jsonResult.technical_design_result));
188
+ if (jsonResult && jsonResult.technical_design_result) {
189
+ console.log('checklist_item_results in parsed JSON:', !!jsonResult.technical_design_result.checklist_item_results);
190
+ if (jsonResult.technical_design_result.checklist_item_results) {
191
+ console.log('checklist_item_results length:', jsonResult.technical_design_result.checklist_item_results
192
+ .length);
193
+ }
194
+ structuredDesignResult = jsonResult.technical_design_result;
195
+ }
196
+ else {
197
+ throw new Error('Invalid JSON structure');
198
+ }
199
+ }
200
+ catch (error) {
201
+ logError(`Failed to parse structured design result: ${error}`);
202
+ // Extract technical design from response if JSON parsing fails
203
+ const extractedDesign = extractTechnicalDesignFromResponse(message.result || lastAssistantResponse);
204
+ structuredDesignResult = {
205
+ status: extractedDesign ? 'success' : 'error',
206
+ technical_design: extractedDesign,
207
+ summary: extractedDesign
208
+ ? 'Technical design generated successfully'
209
+ : 'Failed to generate technical design',
210
+ };
211
+ }
212
+ }
213
+ else {
214
+ logError(`\n⚠️ Technical design generation incomplete: ${message.subtype}`);
215
+ if (message.subtype === 'error_max_turns') {
216
+ logError('💡 Try increasing timeout or reducing complexity');
217
+ }
218
+ // Try to parse results from the last assistant response
219
+ if (lastAssistantResponse) {
220
+ try {
221
+ const responseText = lastAssistantResponse;
222
+ let jsonResult = null;
223
+ const jsonBlockMatch = responseText.match(/```json\s*\n([\s\S]*?)\n\s*```/);
224
+ if (jsonBlockMatch) {
225
+ jsonResult = JSON.parse(jsonBlockMatch[1]);
226
+ if (jsonResult && jsonResult.technical_design_result) {
227
+ structuredDesignResult = jsonResult.technical_design_result;
228
+ }
229
+ }
230
+ else {
231
+ // Try to find JSON object containing "technical_design_result"
232
+ const lines = responseText.split('\n');
233
+ let jsonStartIndex = -1;
234
+ // Find the line that contains the start of a JSON object with "technical_design_result"
235
+ for (let i = 0; i < lines.length; i++) {
236
+ const line = lines[i].trim();
237
+ if (line.startsWith('{') &&
238
+ responseText.includes('"technical_design_result"')) {
239
+ const fromThisLine = lines.slice(i).join('\n');
240
+ if (fromThisLine.includes('"technical_design_result"')) {
241
+ jsonStartIndex = responseText.indexOf(lines[i]);
242
+ break;
243
+ }
244
+ }
245
+ }
246
+ if (jsonStartIndex !== -1) {
247
+ // Find the complete JSON object starting from this position
248
+ let braceCount = 0;
249
+ let endIndex = jsonStartIndex;
250
+ let inString = false;
251
+ let escapeNext = false;
252
+ for (let i = jsonStartIndex; i < responseText.length; i++) {
253
+ const char = responseText[i];
254
+ if (escapeNext) {
255
+ escapeNext = false;
256
+ continue;
257
+ }
258
+ if (char === '\\' && inString) {
259
+ escapeNext = true;
260
+ continue;
261
+ }
262
+ if (char === '"') {
263
+ inString = !inString;
264
+ continue;
265
+ }
266
+ if (!inString) {
267
+ if (char === '{') {
268
+ braceCount++;
269
+ }
270
+ else if (char === '}') {
271
+ braceCount--;
272
+ if (braceCount === 0) {
273
+ endIndex = i;
274
+ break;
275
+ }
276
+ }
277
+ }
278
+ }
279
+ const jsonStr = responseText.substring(jsonStartIndex, endIndex + 1);
280
+ try {
281
+ jsonResult = JSON.parse(jsonStr);
282
+ if (jsonResult && jsonResult.technical_design_result) {
283
+ structuredDesignResult =
284
+ jsonResult.technical_design_result;
285
+ }
286
+ }
287
+ catch (parseError) {
288
+ logError(`Failed to parse extracted JSON: ${parseError}`);
289
+ }
290
+ }
291
+ if (!structuredDesignResult) {
292
+ const extractedDesign = extractTechnicalDesignFromResponse(lastAssistantResponse);
293
+ if (extractedDesign) {
294
+ structuredDesignResult = {
295
+ status: 'success',
296
+ technical_design: extractedDesign,
297
+ summary: 'Technical design generated successfully',
298
+ };
299
+ }
300
+ }
301
+ }
302
+ }
303
+ catch (error) {
304
+ logError(`Failed to parse assistant response: ${error}`);
305
+ }
306
+ }
307
+ }
308
+ }
309
+ }
310
+ // Save the technical design if we have it
311
+ if (structuredDesignResult?.technical_design) {
312
+ if (verbose) {
313
+ logInfo('Saving technical design...');
314
+ }
315
+ const designSaved = await updateTechnicalDesign(mcpServerUrl, mcpToken, featureId, structuredDesignResult.technical_design, verbose);
316
+ // Try HTTP fallback if direct update failed
317
+ if (!designSaved) {
318
+ if (verbose) {
319
+ logInfo('Direct update failed, trying HTTP fallback...');
320
+ }
321
+ const fallbackSaved = await saveTechnicalDesignViaHttp({
322
+ mcpServerUrl,
323
+ mcpToken,
324
+ featureId,
325
+ technicalDesign: structuredDesignResult.technical_design,
326
+ verbose,
327
+ });
328
+ if (!fallbackSaved && verbose) {
329
+ logError('⚠️ Both direct update and HTTP fallback failed');
330
+ }
331
+ return {
332
+ featureId,
333
+ technicalDesign: structuredDesignResult.technical_design,
334
+ status: fallbackSaved ? 'success' : 'error',
335
+ summary: fallbackSaved
336
+ ? 'Technical design generated and saved via HTTP fallback'
337
+ : 'Technical design generated but failed to save',
338
+ savedViaHttp: fallbackSaved,
339
+ data: {
340
+ checklist_item_results: structuredDesignResult.checklist_item_results || [],
341
+ },
342
+ };
343
+ }
344
+ return {
345
+ featureId,
346
+ technicalDesign: structuredDesignResult.technical_design,
347
+ status: 'success',
348
+ summary: 'Technical design generated and saved successfully',
349
+ data: {
350
+ checklist_item_results: structuredDesignResult.checklist_item_results || [],
351
+ },
352
+ };
353
+ }
354
+ return {
355
+ featureId,
356
+ technicalDesign: null,
357
+ status: 'error',
358
+ summary: 'No technical design was generated',
359
+ };
360
+ }
361
+ catch (error) {
362
+ logError(`Technical design generation failed: ${error instanceof Error ? error.message : String(error)}`);
363
+ return {
364
+ featureId,
365
+ technicalDesign: null,
366
+ status: 'error',
367
+ summary: `Generation failed: ${error instanceof Error ? error.message : String(error)}`,
368
+ };
369
+ }
370
+ };
371
+ const extractTechnicalDesignFromResponse = (response) => {
372
+ // Try to extract technical design content from the response
373
+ // Look for markdown sections that contain technical design
374
+ const lines = response.split('\n');
375
+ let inTechnicalDesign = false;
376
+ const technicalDesignLines = [];
377
+ for (let i = 0; i < lines.length; i++) {
378
+ const line = lines[i].toLowerCase();
379
+ // Look for technical design section headers
380
+ if (line.includes('technical design') ||
381
+ line.includes('architecture') ||
382
+ line.includes('# design') ||
383
+ line.includes('## technical')) {
384
+ inTechnicalDesign = true;
385
+ technicalDesignLines.push(lines[i]);
386
+ continue;
387
+ }
388
+ if (inTechnicalDesign) {
389
+ // Continue until we hit another major section or JSON
390
+ if (line.startsWith('{') && line.includes('"technical_design_result"')) {
391
+ break;
392
+ }
393
+ technicalDesignLines.push(lines[i]);
394
+ }
395
+ }
396
+ if (technicalDesignLines.length > 0) {
397
+ return technicalDesignLines.join('\n').trim();
398
+ }
399
+ // Ultimate fallback: return the whole response if it seems to contain design content
400
+ if (response.length > 100 &&
401
+ (response.toLowerCase().includes('architecture') ||
402
+ response.toLowerCase().includes('component') ||
403
+ response.toLowerCase().includes('database'))) {
404
+ return response.trim();
405
+ }
406
+ return null;
407
+ };
408
+ export const checkTechnicalDesignRequirements = async () => {
409
+ try {
410
+ // Check if Claude Code SDK is available
411
+ const claudeCode = await import('@anthropic-ai/claude-code');
412
+ return claudeCode && typeof claudeCode.query === 'function';
413
+ }
414
+ catch (error) {
415
+ console.log('Technical design requirements check failed:', error instanceof Error ? error.message : error);
416
+ return false;
417
+ }
418
+ };
@@ -0,0 +1,12 @@
1
+ import type { FeatureInfo, UserStory, TestCase } from '../../types/features.js';
2
+ import { type ProductInfo } from '../../api/products.js';
3
+ export interface TechnicalDesignContext {
4
+ feature: FeatureInfo;
5
+ product: ProductInfo;
6
+ user_stories: UserStory[];
7
+ test_cases: TestCase[];
8
+ }
9
+ /**
10
+ * Fetch all technical design context information via MCP endpoints
11
+ */
12
+ export declare function fetchTechnicalDesignContext(mcpServerUrl: string, mcpToken: string, featureId: string, verbose?: boolean): Promise<TechnicalDesignContext>;
@@ -0,0 +1,39 @@
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
+ /**
5
+ * Fetch all technical design context information via MCP endpoints
6
+ */
7
+ export async function fetchTechnicalDesignContext(mcpServerUrl, mcpToken, featureId, verbose) {
8
+ try {
9
+ if (verbose) {
10
+ logInfo(`Fetching complete technical design context for feature: ${featureId}`);
11
+ }
12
+ // Fetch all required data in parallel for better performance
13
+ const [feature, user_stories, test_cases] = await Promise.all([
14
+ getFeature(mcpServerUrl, mcpToken, featureId, verbose),
15
+ getUserStories(mcpServerUrl, mcpToken, featureId, verbose),
16
+ getTestCases(mcpServerUrl, mcpToken, featureId, verbose),
17
+ ]);
18
+ const product = await getProduct(mcpServerUrl, mcpToken, feature.product_id, verbose);
19
+ if (verbose) {
20
+ logInfo(`✅ Technical design context fetched successfully:`);
21
+ logInfo(` Feature: ${feature.name}`);
22
+ logInfo(` Product: ${product.name}`);
23
+ logInfo(` User Stories: ${user_stories.length}`);
24
+ logInfo(` Test Cases: ${test_cases.length} (${test_cases.filter((tc) => tc.is_critical).length} critical)`);
25
+ logInfo(` Existing Technical Design: ${feature.technical_design ? 'Yes' : 'No'}`);
26
+ }
27
+ return {
28
+ feature,
29
+ product,
30
+ user_stories,
31
+ test_cases,
32
+ };
33
+ }
34
+ catch (error) {
35
+ const errorMessage = error instanceof Error ? error.message : String(error);
36
+ logError(`Failed to fetch technical design context: ${errorMessage}`);
37
+ throw new Error(`Context fetch failed: ${errorMessage}`);
38
+ }
39
+ }
@@ -1,4 +1,4 @@
1
- import { logInfo, logError } from '../logger.js';
1
+ import { logInfo, logError } from '../../utils/logger.js';
2
2
  /**
3
3
  * Save technical design via HTTP as a fallback when MCP server fails
4
4
  */