edsger 0.2.1 → 0.2.3

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