@tpitre/story-ui 2.1.5 → 2.3.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 (203) hide show
  1. package/.env.sample +82 -11
  2. package/README.md +130 -4
  3. package/dist/cli/deploy.d.ts +17 -0
  4. package/dist/cli/deploy.d.ts.map +1 -0
  5. package/dist/cli/deploy.js +696 -0
  6. package/dist/cli/index.d.ts +3 -0
  7. package/dist/cli/index.d.ts.map +1 -0
  8. package/dist/cli/index.js +55 -2
  9. package/dist/cli/setup.d.ts +11 -0
  10. package/dist/cli/setup.d.ts.map +1 -0
  11. package/dist/cli/setup.js +437 -110
  12. package/dist/mcp-server/index.d.ts +2 -0
  13. package/dist/mcp-server/index.d.ts.map +1 -0
  14. package/dist/mcp-server/index.js +138 -6
  15. package/dist/mcp-server/mcp-stdio-server.d.ts +3 -0
  16. package/dist/mcp-server/mcp-stdio-server.d.ts.map +1 -0
  17. package/dist/mcp-server/mcp-stdio-server.js +638 -0
  18. package/dist/mcp-server/routes/claude.d.ts +3 -0
  19. package/dist/mcp-server/routes/claude.d.ts.map +1 -0
  20. package/dist/mcp-server/routes/claude.js +60 -23
  21. package/dist/mcp-server/routes/components.d.ts +4 -0
  22. package/dist/mcp-server/routes/components.d.ts.map +1 -0
  23. package/dist/mcp-server/routes/frameworks.d.ts +38 -0
  24. package/dist/mcp-server/routes/frameworks.d.ts.map +1 -0
  25. package/dist/mcp-server/routes/frameworks.js +183 -0
  26. package/dist/mcp-server/routes/generateStory.d.ts +3 -0
  27. package/dist/mcp-server/routes/generateStory.d.ts.map +1 -0
  28. package/dist/mcp-server/routes/generateStory.js +274 -115
  29. package/dist/mcp-server/routes/generateStoryStream.d.ts +12 -0
  30. package/dist/mcp-server/routes/generateStoryStream.d.ts.map +1 -0
  31. package/dist/mcp-server/routes/generateStoryStream.js +947 -0
  32. package/dist/mcp-server/routes/hybridStories.d.ts +18 -0
  33. package/dist/mcp-server/routes/hybridStories.d.ts.map +1 -0
  34. package/dist/mcp-server/routes/hybridStories.js +214 -0
  35. package/dist/mcp-server/routes/mcpRemote.d.ts +14 -0
  36. package/dist/mcp-server/routes/mcpRemote.d.ts.map +1 -0
  37. package/dist/mcp-server/routes/mcpRemote.js +489 -0
  38. package/dist/mcp-server/routes/memoryStories.d.ts +26 -0
  39. package/dist/mcp-server/routes/memoryStories.d.ts.map +1 -0
  40. package/dist/mcp-server/routes/memoryStories.js +13 -7
  41. package/dist/mcp-server/routes/providers.d.ts +89 -0
  42. package/dist/mcp-server/routes/providers.d.ts.map +1 -0
  43. package/dist/mcp-server/routes/providers.js +369 -0
  44. package/dist/mcp-server/routes/storySync.d.ts +26 -0
  45. package/dist/mcp-server/routes/storySync.d.ts.map +1 -0
  46. package/dist/mcp-server/routes/streamTypes.d.ts +110 -0
  47. package/dist/mcp-server/routes/streamTypes.d.ts.map +1 -0
  48. package/dist/mcp-server/routes/streamTypes.js +18 -0
  49. package/dist/mcp-server/sessionManager.d.ts +50 -0
  50. package/dist/mcp-server/sessionManager.d.ts.map +1 -0
  51. package/dist/mcp-server/sessionManager.js +125 -0
  52. package/dist/story-generator/componentBlacklist.d.ts +21 -0
  53. package/dist/story-generator/componentBlacklist.d.ts.map +1 -0
  54. package/dist/story-generator/componentBlacklist.js +4 -0
  55. package/dist/story-generator/componentDiscovery.d.ts +28 -0
  56. package/dist/story-generator/componentDiscovery.d.ts.map +1 -0
  57. package/dist/story-generator/componentRegistryGenerator.d.ts +49 -0
  58. package/dist/story-generator/componentRegistryGenerator.d.ts.map +1 -0
  59. package/dist/story-generator/componentRegistryGenerator.js +205 -0
  60. package/dist/story-generator/configLoader.d.ts +33 -0
  61. package/dist/story-generator/configLoader.d.ts.map +1 -0
  62. package/dist/story-generator/configLoader.js +8 -1
  63. package/dist/story-generator/considerationsLoader.d.ts +32 -0
  64. package/dist/story-generator/considerationsLoader.d.ts.map +1 -0
  65. package/dist/story-generator/considerationsLoader.js +2 -1
  66. package/dist/story-generator/documentation-sources.d.ts +28 -0
  67. package/dist/story-generator/documentation-sources.d.ts.map +1 -0
  68. package/dist/story-generator/documentationLoader.d.ts +64 -0
  69. package/dist/story-generator/documentationLoader.d.ts.map +1 -0
  70. package/dist/story-generator/documentationLoader.js +4 -3
  71. package/dist/story-generator/dynamicPackageDiscovery.d.ts +97 -0
  72. package/dist/story-generator/dynamicPackageDiscovery.d.ts.map +1 -0
  73. package/dist/story-generator/dynamicPackageDiscovery.js +31 -22
  74. package/dist/story-generator/enhancedComponentDiscovery.d.ts +125 -0
  75. package/dist/story-generator/enhancedComponentDiscovery.d.ts.map +1 -0
  76. package/dist/story-generator/enhancedComponentDiscovery.js +162 -21
  77. package/dist/story-generator/framework-adapters/angular-adapter.d.ts +40 -0
  78. package/dist/story-generator/framework-adapters/angular-adapter.d.ts.map +1 -0
  79. package/dist/story-generator/framework-adapters/angular-adapter.js +427 -0
  80. package/dist/story-generator/framework-adapters/base-adapter.d.ts +75 -0
  81. package/dist/story-generator/framework-adapters/base-adapter.d.ts.map +1 -0
  82. package/dist/story-generator/framework-adapters/base-adapter.js +147 -0
  83. package/dist/story-generator/framework-adapters/framework-detector.d.ts +55 -0
  84. package/dist/story-generator/framework-adapters/framework-detector.d.ts.map +1 -0
  85. package/dist/story-generator/framework-adapters/framework-detector.js +323 -0
  86. package/dist/story-generator/framework-adapters/index.d.ts +97 -0
  87. package/dist/story-generator/framework-adapters/index.d.ts.map +1 -0
  88. package/dist/story-generator/framework-adapters/index.js +198 -0
  89. package/dist/story-generator/framework-adapters/react-adapter.d.ts +40 -0
  90. package/dist/story-generator/framework-adapters/react-adapter.d.ts.map +1 -0
  91. package/dist/story-generator/framework-adapters/react-adapter.js +316 -0
  92. package/dist/story-generator/framework-adapters/svelte-adapter.d.ts +40 -0
  93. package/dist/story-generator/framework-adapters/svelte-adapter.d.ts.map +1 -0
  94. package/dist/story-generator/framework-adapters/svelte-adapter.js +372 -0
  95. package/dist/story-generator/framework-adapters/types.d.ts +182 -0
  96. package/dist/story-generator/framework-adapters/types.d.ts.map +1 -0
  97. package/dist/story-generator/framework-adapters/types.js +8 -0
  98. package/dist/story-generator/framework-adapters/vue-adapter.d.ts +36 -0
  99. package/dist/story-generator/framework-adapters/vue-adapter.d.ts.map +1 -0
  100. package/dist/story-generator/framework-adapters/vue-adapter.js +336 -0
  101. package/dist/story-generator/framework-adapters/web-components-adapter.d.ts +54 -0
  102. package/dist/story-generator/framework-adapters/web-components-adapter.d.ts.map +1 -0
  103. package/dist/story-generator/framework-adapters/web-components-adapter.js +387 -0
  104. package/dist/story-generator/generateStory.d.ts +7 -0
  105. package/dist/story-generator/generateStory.d.ts.map +1 -0
  106. package/dist/story-generator/gitignoreManager.d.ts +50 -0
  107. package/dist/story-generator/gitignoreManager.d.ts.map +1 -0
  108. package/dist/story-generator/gitignoreManager.js +7 -6
  109. package/dist/story-generator/imageProcessor.d.ts +80 -0
  110. package/dist/story-generator/imageProcessor.d.ts.map +1 -0
  111. package/dist/story-generator/imageProcessor.js +391 -0
  112. package/dist/story-generator/inMemoryStoryService.d.ts +89 -0
  113. package/dist/story-generator/inMemoryStoryService.d.ts.map +1 -0
  114. package/dist/story-generator/llm-providers/base-provider.d.ts +36 -0
  115. package/dist/story-generator/llm-providers/base-provider.d.ts.map +1 -0
  116. package/dist/story-generator/llm-providers/base-provider.js +135 -0
  117. package/dist/story-generator/llm-providers/claude-provider.d.ts +23 -0
  118. package/dist/story-generator/llm-providers/claude-provider.d.ts.map +1 -0
  119. package/dist/story-generator/llm-providers/claude-provider.js +414 -0
  120. package/dist/story-generator/llm-providers/gemini-provider.d.ts +24 -0
  121. package/dist/story-generator/llm-providers/gemini-provider.d.ts.map +1 -0
  122. package/dist/story-generator/llm-providers/gemini-provider.js +406 -0
  123. package/dist/story-generator/llm-providers/index.d.ts +63 -0
  124. package/dist/story-generator/llm-providers/index.d.ts.map +1 -0
  125. package/dist/story-generator/llm-providers/index.js +169 -0
  126. package/dist/story-generator/llm-providers/openai-provider.d.ts +24 -0
  127. package/dist/story-generator/llm-providers/openai-provider.d.ts.map +1 -0
  128. package/dist/story-generator/llm-providers/openai-provider.js +458 -0
  129. package/dist/story-generator/llm-providers/settings-manager.d.ts +75 -0
  130. package/dist/story-generator/llm-providers/settings-manager.d.ts.map +1 -0
  131. package/dist/story-generator/llm-providers/settings-manager.js +173 -0
  132. package/dist/story-generator/llm-providers/story-llm-service.d.ts +79 -0
  133. package/dist/story-generator/llm-providers/story-llm-service.d.ts.map +1 -0
  134. package/dist/story-generator/llm-providers/story-llm-service.js +240 -0
  135. package/dist/story-generator/llm-providers/types.d.ts +153 -0
  136. package/dist/story-generator/llm-providers/types.d.ts.map +1 -0
  137. package/dist/story-generator/llm-providers/types.js +8 -0
  138. package/dist/story-generator/logger.d.ts +14 -0
  139. package/dist/story-generator/logger.d.ts.map +1 -0
  140. package/dist/story-generator/logger.js +119 -0
  141. package/dist/story-generator/postProcessStory.d.ts +6 -0
  142. package/dist/story-generator/postProcessStory.d.ts.map +1 -0
  143. package/dist/story-generator/postProcessStory.js +8 -7
  144. package/dist/story-generator/productionGitignoreManager.d.ts +91 -0
  145. package/dist/story-generator/productionGitignoreManager.d.ts.map +1 -0
  146. package/dist/story-generator/productionGitignoreManager.js +11 -10
  147. package/dist/story-generator/promptGenerator.d.ts +48 -0
  148. package/dist/story-generator/promptGenerator.d.ts.map +1 -0
  149. package/dist/story-generator/promptGenerator.js +186 -1
  150. package/dist/story-generator/storyHistory.d.ts +44 -0
  151. package/dist/story-generator/storyHistory.d.ts.map +1 -0
  152. package/dist/story-generator/storySync.d.ts +68 -0
  153. package/dist/story-generator/storySync.d.ts.map +1 -0
  154. package/dist/story-generator/storyTracker.d.ts +48 -0
  155. package/dist/story-generator/storyTracker.d.ts.map +1 -0
  156. package/dist/story-generator/storyTracker.js +2 -1
  157. package/dist/story-generator/storyValidator.d.ts +6 -0
  158. package/dist/story-generator/storyValidator.d.ts.map +1 -0
  159. package/dist/story-generator/universalDesignSystemAdapter.d.ts +68 -0
  160. package/dist/story-generator/universalDesignSystemAdapter.d.ts.map +1 -0
  161. package/dist/story-generator/universalDesignSystemAdapter.js +141 -3
  162. package/dist/story-generator/urlRedirectService.d.ts +21 -0
  163. package/dist/story-generator/urlRedirectService.d.ts.map +1 -0
  164. package/dist/story-generator/urlRedirectService.js +140 -0
  165. package/dist/story-generator/validateStory.d.ts +19 -0
  166. package/dist/story-generator/validateStory.d.ts.map +1 -0
  167. package/dist/story-generator/validateStory.js +6 -2
  168. package/dist/story-generator/visionPrompts.d.ts +88 -0
  169. package/dist/story-generator/visionPrompts.d.ts.map +1 -0
  170. package/dist/story-generator/visionPrompts.js +462 -0
  171. package/dist/story-ui.config.d.ts +78 -0
  172. package/dist/story-ui.config.d.ts.map +1 -0
  173. package/dist/templates/StoryUI/StoryUIPanel.d.ts +4 -0
  174. package/dist/templates/StoryUI/StoryUIPanel.d.ts.map +1 -0
  175. package/dist/templates/StoryUI/StoryUIPanel.js +1874 -0
  176. package/dist/templates/StoryUI/StoryUIPanel.stories.d.ts +18 -0
  177. package/dist/templates/StoryUI/StoryUIPanel.stories.d.ts.map +1 -0
  178. package/dist/templates/StoryUI/StoryUIPanel.stories.js +37 -0
  179. package/dist/templates/StoryUI/index.d.ts +3 -0
  180. package/dist/templates/StoryUI/index.d.ts.map +1 -0
  181. package/dist/templates/StoryUI/index.js +2 -0
  182. package/package.json +35 -4
  183. package/templates/StoryUI/StoryUIPanel.tsx +1973 -388
  184. package/templates/StoryUI/index.tsx +1 -1
  185. package/templates/StoryUI/manager.tsx +264 -0
  186. package/templates/mcp-config-claude.json +11 -0
  187. package/templates/mcp-example.md +76 -0
  188. package/templates/production-app/.env.example +11 -0
  189. package/templates/production-app/index.html +66 -0
  190. package/templates/production-app/package.json +30 -0
  191. package/templates/production-app/public/favicon.svg +5 -0
  192. package/templates/production-app/src/App.tsx +1157 -0
  193. package/templates/production-app/src/LivePreviewRenderer.tsx +420 -0
  194. package/templates/production-app/src/componentRegistry.ts +315 -0
  195. package/templates/production-app/src/considerations.ts +16 -0
  196. package/templates/production-app/src/index.css +284 -0
  197. package/templates/production-app/src/main.tsx +25 -0
  198. package/templates/production-app/tsconfig.json +32 -0
  199. package/templates/production-app/tsconfig.node.json +11 -0
  200. package/templates/production-app/vite.config.ts +83 -0
  201. package/templates/react-import-rule.json +2 -2
  202. package/dist/index.js +0 -12
  203. package/dist/story-ui.config.loader.js +0 -205
@@ -1,11 +1,12 @@
1
- import fetch from 'node-fetch';
2
1
  import { generateStory } from '../../story-generator/generateStory.js';
3
2
  import * as crypto from 'crypto';
4
- import { buildClaudePrompt as buildFlexiblePrompt } from '../../story-generator/promptGenerator.js';
3
+ import * as path from 'path';
4
+ import { buildClaudePrompt as buildFlexiblePrompt, buildFrameworkAwarePrompt, detectProjectFramework, } from '../../story-generator/promptGenerator.js';
5
+ import { getAdapter } from '../../story-generator/framework-adapters/index.js';
5
6
  import { loadUserConfig, validateConfig } from '../../story-generator/configLoader.js';
6
7
  import { setupProductionGitignore } from '../../story-generator/productionGitignoreManager.js';
7
8
  import { getInMemoryStoryService } from '../../story-generator/inMemoryStoryService.js';
8
- import { extractAndValidateCodeBlock, createFallbackStory } from '../../story-generator/validateStory.js';
9
+ import { extractAndValidateCodeBlock, createFallbackStory, validateStoryCode } from '../../story-generator/validateStory.js';
9
10
  import { isBlacklistedComponent, isBlacklistedIcon, getBlacklistErrorMessage, ICON_CORRECTIONS } from '../../story-generator/componentBlacklist.js';
10
11
  import { StoryTracker } from '../../story-generator/storyTracker.js';
11
12
  import { EnhancedComponentDiscovery } from '../../story-generator/enhancedComponentDiscovery.js';
@@ -13,13 +14,11 @@ import { getDocumentation } from '../../story-generator/documentation-sources.js
13
14
  import { postProcessStory } from '../../story-generator/postProcessStory.js';
14
15
  import { validateStory } from '../../story-generator/storyValidator.js';
15
16
  import { StoryHistoryManager } from '../../story-generator/storyHistory.js';
16
- const CLAUDE_API_URL = 'https://api.anthropic.com/v1/messages';
17
- const CLAUDE_MODEL = process.env.CLAUDE_MODEL || 'claude-sonnet-4-20250514';
18
- // Legacy constants - now using dynamic discovery
19
- const COMPONENT_LIST = [];
20
- const SAMPLE_STORY = '';
21
- // Legacy component reference - now using dynamic discovery
22
- const COMPONENT_REFERENCE = '';
17
+ import { logger } from '../../story-generator/logger.js';
18
+ import { UrlRedirectService } from '../../story-generator/urlRedirectService.js';
19
+ import { chatCompletion, generateTitle as llmGenerateTitle, isProviderConfigured, getProviderInfo, chatCompletionWithImages, buildMessageWithImages } from '../../story-generator/llm-providers/story-llm-service.js';
20
+ import { processImageInputs } from '../../story-generator/imageProcessor.js';
21
+ import { buildVisionAwarePrompt } from '../../story-generator/visionPrompts.js';
23
22
  // Legacy function - now uses flexible system with enhanced discovery
24
23
  async function buildClaudePrompt(userPrompt) {
25
24
  const config = loadUserConfig();
@@ -28,17 +27,60 @@ async function buildClaudePrompt(userPrompt) {
28
27
  return await buildFlexiblePrompt(userPrompt, config, components);
29
28
  }
30
29
  // Enhanced function that includes conversation context and previous code
31
- async function buildClaudePromptWithContext(userPrompt, config, conversation, previousCode) {
30
+ // Now supports multi-framework prompt generation and vision-aware prompts
31
+ async function buildClaudePromptWithContext(userPrompt, config, conversation, previousCode, options) {
32
32
  const discovery = new EnhancedComponentDiscovery(config);
33
33
  const components = await discovery.discoverAll();
34
+ // Determine if we should use framework-aware prompts
35
+ let useFrameworkAware = false;
36
+ let frameworkOptions;
37
+ if (options?.framework) {
38
+ // Explicit framework specified
39
+ useFrameworkAware = true;
40
+ frameworkOptions = { framework: options.framework };
41
+ logger.log(`📦 Using specified framework: ${options.framework}`);
42
+ }
43
+ else if (options?.autoDetectFramework) {
44
+ // Auto-detect framework from project
45
+ try {
46
+ const detectedFramework = await detectProjectFramework(process.cwd());
47
+ useFrameworkAware = true;
48
+ frameworkOptions = { framework: detectedFramework };
49
+ logger.log(`📦 Auto-detected framework: ${detectedFramework}`);
50
+ }
51
+ catch (error) {
52
+ logger.warn('Failed to auto-detect framework, using React default', { error });
53
+ }
54
+ }
34
55
  // Always start with component discovery as the authoritative source
35
- console.log(`📦 Discovered ${components.length} components from ${config.importPath}`);
56
+ logger.log(`📦 Discovered ${components.length} components from ${config.importPath}`);
36
57
  const availableComponents = components.map(c => c.name).join(', ');
37
- console.log(`✅ Available components: ${availableComponents}`);
58
+ logger.log(`✅ Available components: ${availableComponents}`);
38
59
  // Build base prompt with discovered components (always required)
39
- let prompt = await buildFlexiblePrompt(userPrompt, config, components);
60
+ // Use framework-aware prompt if configured, otherwise use legacy React prompt
61
+ let prompt;
62
+ if (useFrameworkAware && frameworkOptions) {
63
+ prompt = await buildFrameworkAwarePrompt(userPrompt, config, components, frameworkOptions);
64
+ logger.log(`🔧 Built framework-aware prompt for ${frameworkOptions.framework}`);
65
+ }
66
+ else {
67
+ prompt = await buildFlexiblePrompt(userPrompt, config, components);
68
+ }
69
+ // Enhance prompt with vision-aware context if vision mode is provided
70
+ if (options?.visionMode) {
71
+ logger.log(`🔍 Enhancing prompt with vision mode: ${options.visionMode}`);
72
+ const visionPrompts = buildVisionAwarePrompt({
73
+ promptType: options.visionMode,
74
+ userDescription: userPrompt,
75
+ availableComponents: components.map(c => c.name),
76
+ framework: frameworkOptions?.framework || 'react',
77
+ designSystem: options.designSystem,
78
+ });
79
+ // Combine the vision system prompt with the existing prompt and add the user prompt
80
+ prompt = `${visionPrompts.systemPrompt}\n\n---\n\n${prompt}\n\n---\n\n${visionPrompts.userPrompt}`;
81
+ }
40
82
  // Try to enhance with bundled documentation for usage patterns and design tokens
41
- console.log('📋 Using bundled documentation for enhancement');
83
+ logger.log('📋 Using bundled documentation for enhancement');
42
84
  const documentation = getDocumentation(config.importPath);
43
85
  if (documentation) {
44
86
  const bundledEnhancement = `
@@ -112,26 +154,32 @@ function extractCodeBlock(text) {
112
154
  const codeBlock = text.match(/```(?:tsx|jsx|typescript|ts|js|javascript)?([\s\S]*?)```/i);
113
155
  return codeBlock ? codeBlock[1].trim() : null;
114
156
  }
115
- async function callClaude(messages) {
116
- const apiKey = process.env.CLAUDE_API_KEY;
117
- if (!apiKey)
118
- throw new Error('Claude API key not set');
119
- const response = await fetch(CLAUDE_API_URL, {
120
- method: 'POST',
121
- headers: {
122
- 'x-api-key': apiKey,
123
- 'content-type': 'application/json',
124
- 'anthropic-version': '2023-06-01',
125
- },
126
- body: JSON.stringify({
127
- model: CLAUDE_MODEL,
128
- max_tokens: 8192,
129
- messages,
130
- }),
131
- });
132
- const data = await response.json();
133
- // Try to extract the main content
134
- return data?.content?.[0]?.text || data?.completion || '';
157
+ async function callLLM(messages, images) {
158
+ // Check if any provider is configured
159
+ if (!isProviderConfigured()) {
160
+ throw new Error('No LLM provider configured. Please set CLAUDE_API_KEY, OPENAI_API_KEY, or GEMINI_API_KEY.');
161
+ }
162
+ const providerInfo = getProviderInfo();
163
+ logger.debug(`Using ${providerInfo.currentProvider} (${providerInfo.currentModel}) for story generation`);
164
+ // If images are provided, use vision-capable chat
165
+ if (images && images.length > 0) {
166
+ if (!providerInfo.supportsVision) {
167
+ throw new Error(`${providerInfo.currentProvider} does not support vision. Please configure a vision-capable provider.`);
168
+ }
169
+ logger.log(`🖼️ Using vision-capable chat with ${images.length} image(s)`);
170
+ // Convert messages to include images in the first user message
171
+ const messagesWithImages = messages.map((msg, index) => {
172
+ if (msg.role === 'user' && index === 0) {
173
+ return {
174
+ role: msg.role,
175
+ content: buildMessageWithImages(msg.content, images)
176
+ };
177
+ }
178
+ return msg;
179
+ });
180
+ return await chatCompletionWithImages(messagesWithImages, { maxTokens: 8192 });
181
+ }
182
+ return await chatCompletion(messages, { maxTokens: 8192 });
135
183
  }
136
184
  function cleanPromptForTitle(prompt) {
137
185
  if (!prompt || typeof prompt !== 'string') {
@@ -168,34 +216,15 @@ function cleanPromptForTitle(prompt) {
168
216
  .trim()
169
217
  .replace(/\b\w/g, c => c.toUpperCase()); // Capitalize words
170
218
  }
171
- async function getClaudeTitle(userPrompt) {
172
- const titlePrompt = [
173
- "Given the following UI description, generate a short, clear, human-friendly title suitable for a Storybook navigation item.",
174
- "Requirements:",
175
- "- Do not include words like 'Generate', 'Build', or 'Create'",
176
- "- Keep it under 50 characters",
177
- "- Use simple, clear language",
178
- "- Avoid special characters that could break code (use letters, numbers, spaces, hyphens, and basic punctuation only)",
179
- '',
180
- 'UI description:',
181
- userPrompt,
182
- '',
183
- 'Title:'
184
- ].join('\n');
185
- const aiText = await callClaude([{ role: 'user', content: titlePrompt }]);
186
- // Take the first non-empty line, trim, and remove quotes if present
187
- const lines = aiText.split('\n').map(l => l.trim()).filter(Boolean);
188
- if (lines.length > 0) {
189
- let title = lines[0].replace(/^['\"]|['\"]$/g, '').trim();
190
- // Additional sanitization for safety
191
- title = title
192
- .replace(/[^\w\s'"?!-]/g, ' ') // Remove problematic characters
193
- .replace(/\s+/g, ' ') // Normalize whitespace
194
- .trim()
195
- .slice(0, 50); // Limit length
196
- return title;
219
+ async function getLLMTitle(userPrompt) {
220
+ // Use the LLM service's built-in title generation
221
+ try {
222
+ return await llmGenerateTitle(userPrompt);
223
+ }
224
+ catch (error) {
225
+ logger.warn('Failed to generate title via LLM, using fallback', { error });
226
+ return '';
197
227
  }
198
- return '';
199
228
  }
200
229
  function escapeTitleForTS(title) {
201
230
  // Escape all characters that could break TypeScript string literals
@@ -313,7 +342,12 @@ function fileNameFromTitle(title, hash) {
313
342
  return `${base}-${hash}.stories.tsx`;
314
343
  }
315
344
  export async function generateStoryFromPrompt(req, res) {
316
- const { prompt, fileName, conversation } = req.body;
345
+ const { prompt, fileName, conversation, isUpdate, originalTitle, storyId: providedStoryId, framework, // Explicit framework (react, vue, angular, svelte, web-components)
346
+ autoDetectFramework, // Auto-detect from project (default: false)
347
+ images, // Array of images for vision-based generation
348
+ visionMode, // Vision mode: 'screenshot_to_story', 'design_to_story', 'component_analysis', 'layout_analysis'
349
+ designSystem // Design system being used (chakra-ui, mantine, etc.)
350
+ } = req.body;
317
351
  if (!prompt)
318
352
  return res.status(400).json({ error: 'Missing prompt' });
319
353
  try {
@@ -326,6 +360,21 @@ export async function generateStoryFromPrompt(req, res) {
326
360
  details: validation.errors
327
361
  });
328
362
  }
363
+ // Process images if provided
364
+ let processedImages = [];
365
+ if (images && Array.isArray(images) && images.length > 0) {
366
+ logger.log(`📸 Processing ${images.length} image(s) for vision-based story generation`);
367
+ try {
368
+ processedImages = await processImageInputs(images);
369
+ logger.log(`✅ Successfully processed ${processedImages.length} image(s)`);
370
+ }
371
+ catch (imageError) {
372
+ return res.status(400).json({
373
+ error: 'Image processing failed',
374
+ details: imageError instanceof Error ? imageError.message : String(imageError)
375
+ });
376
+ }
377
+ }
329
378
  // Set up production-ready environment
330
379
  const gitignoreManager = setupProductionGitignore(config);
331
380
  const storyService = getInMemoryStoryService(config);
@@ -334,17 +383,36 @@ export async function generateStoryFromPrompt(req, res) {
334
383
  const storyTracker = new StoryTracker(config);
335
384
  // Initialize history manager - use the current working directory
336
385
  const historyManager = new StoryHistoryManager(process.cwd());
386
+ // Initialize URL redirect service
387
+ // Use the same directory as the stories to ensure consistency
388
+ const redirectDir = isProduction ? process.cwd() : path.dirname(config.generatedStoriesPath);
389
+ const redirectService = new UrlRedirectService(redirectDir);
337
390
  // Check if this is an update to an existing story
338
- const isUpdate = fileName && conversation && conversation.length > 2;
391
+ // Use the explicit isUpdate flag from request, or fallback to old logic
392
+ const isActualUpdate = req.body.isUpdate || (fileName && conversation && conversation.length > 2);
339
393
  // Get previous code if this is an update
340
394
  let previousCode;
341
395
  let parentVersionId;
342
- if (isUpdate && fileName) {
396
+ let oldTitle;
397
+ let oldStoryUrl;
398
+ if (isActualUpdate && fileName) {
343
399
  const currentVersion = historyManager.getCurrentVersion(fileName);
344
400
  if (currentVersion) {
345
401
  previousCode = currentVersion.code;
346
402
  parentVersionId = currentVersion.id;
347
- console.log('🔄 Found previous version for iteration');
403
+ logger.log('🔄 Found previous version for iteration');
404
+ // Extract the old title from previous code
405
+ const titleMatch = previousCode.match(/title:\s*["']([^"']+)['"]/);
406
+ if (titleMatch) {
407
+ oldTitle = titleMatch[1];
408
+ // Remove the prefix to get clean title for URL generation
409
+ const cleanOldTitle = oldTitle.replace(config.storyPrefix || 'Generated/', '');
410
+ // Convert title to Storybook URL format
411
+ oldStoryUrl = `/story/${cleanOldTitle.toLowerCase().replace(/[^a-z0-9]+/g, '-')}--primary`;
412
+ logger.log('📌 Previous title:', oldTitle);
413
+ logger.log('📌 Clean title for URL:', cleanOldTitle);
414
+ logger.log('📌 Previous URL:', oldStoryUrl);
415
+ }
348
416
  }
349
417
  }
350
418
  // --- Start of Validation and Retry Loop ---
@@ -352,17 +420,24 @@ export async function generateStoryFromPrompt(req, res) {
352
420
  let validationErrors = [];
353
421
  const maxRetries = 3;
354
422
  let attempts = 0;
355
- const initialPrompt = await buildClaudePromptWithContext(prompt, config, conversation, previousCode);
423
+ // Build framework-aware options with vision support
424
+ const frameworkOptions = {
425
+ framework: framework,
426
+ autoDetectFramework: autoDetectFramework === true,
427
+ visionMode: visionMode,
428
+ designSystem: designSystem,
429
+ };
430
+ const initialPrompt = await buildClaudePromptWithContext(prompt, config, conversation, previousCode, frameworkOptions);
356
431
  const messages = [{ role: 'user', content: initialPrompt }];
357
432
  while (attempts < maxRetries) {
358
433
  attempts++;
359
- console.log(`--- Story Generation Attempt ${attempts} ---`);
360
- const claudeResponse = await callClaude(messages);
434
+ logger.log(`--- Story Generation Attempt ${attempts} ---`);
435
+ const claudeResponse = await callLLM(messages, processedImages.length > 0 ? processedImages : undefined);
361
436
  const extractedCode = extractCodeBlock(claudeResponse);
362
437
  if (!extractedCode) {
363
438
  aiText = claudeResponse; // Use raw response if no code block
364
439
  if (attempts < maxRetries) {
365
- console.log('No code block found, retrying...');
440
+ logger.log('No code block found, retrying...');
366
441
  messages.push({ role: 'assistant', content: aiText });
367
442
  messages.push({ role: 'user', content: 'You did not provide a code block. Please provide the complete story in a single `tsx` code block.' });
368
443
  continue;
@@ -377,11 +452,11 @@ export async function generateStoryFromPrompt(req, res) {
377
452
  }
378
453
  validationErrors = validateStory(aiText);
379
454
  if (validationErrors.length === 0) {
380
- console.log('✅ Validation successful!');
455
+ logger.log('✅ Validation successful!');
381
456
  break; // Exit loop on success
382
457
  }
383
- console.log(`❌ Validation failed with ${validationErrors.length} errors:`);
384
- validationErrors.forEach(err => console.log(` - Line ${err.line}: ${err.message}`));
458
+ logger.log(`❌ Validation failed with ${validationErrors.length} errors:`);
459
+ validationErrors.forEach(err => logger.log(` - Line ${err.line}: ${err.message}`));
385
460
  if (attempts < maxRetries) {
386
461
  const errorFeedback = validationErrors
387
462
  .map(err => `- Line ${err.line}: ${err.message}`)
@@ -397,7 +472,7 @@ export async function generateStoryFromPrompt(req, res) {
397
472
  // For now, we'll proceed with the last attempt and let the user see the result
398
473
  }
399
474
  // --- End of Validation and Retry Loop ---
400
- console.log('Claude final response:', aiText);
475
+ logger.log('Claude final response:', aiText);
401
476
  // Create enhanced component discovery for validation
402
477
  const discovery = new EnhancedComponentDiscovery(config);
403
478
  await discovery.discoverAll();
@@ -416,7 +491,7 @@ export async function generateStoryFromPrompt(req, res) {
416
491
  const validationResult = extractAndValidateCodeBlock(aiText, config);
417
492
  let fileContents;
418
493
  let hasValidationWarnings = false;
419
- console.log('Validation result:', {
494
+ logger.log('Validation result:', {
420
495
  isValid: validationResult.isValid,
421
496
  errors: validationResult.errors,
422
497
  warnings: validationResult.warnings,
@@ -425,7 +500,7 @@ export async function generateStoryFromPrompt(req, res) {
425
500
  if (!validationResult.isValid && !validationResult.fixedCode) {
426
501
  console.error('Generated code validation failed:', validationResult.errors);
427
502
  // Create fallback story only if we can't fix the code
428
- console.log('Creating fallback story due to validation failure');
503
+ logger.log('Creating fallback story due to validation failure');
429
504
  fileContents = createFallbackStory(prompt, config);
430
505
  hasValidationWarnings = true;
431
506
  }
@@ -434,7 +509,7 @@ export async function generateStoryFromPrompt(req, res) {
434
509
  if (validationResult.fixedCode) {
435
510
  fileContents = validationResult.fixedCode;
436
511
  hasValidationWarnings = true;
437
- console.log('Using auto-fixed code');
512
+ logger.log('Using auto-fixed code');
438
513
  }
439
514
  else {
440
515
  // Extract the validated code
@@ -455,28 +530,46 @@ export async function generateStoryFromPrompt(req, res) {
455
530
  }
456
531
  if (validationResult.warnings && validationResult.warnings.length > 0) {
457
532
  hasValidationWarnings = true;
458
- console.log('Validation warnings:', validationResult.warnings);
533
+ logger.log('Validation warnings:', validationResult.warnings);
459
534
  }
460
535
  }
461
536
  if (!fileContents) {
462
537
  console.error('No valid code could be extracted or generated.');
463
538
  return res.status(500).json({ error: 'Failed to generate valid TypeScript code.' });
464
539
  }
465
- // CRITICAL: Ensure React import exists but avoid duplicates
466
- if (!fileContents.includes("import React from 'react';")) {
540
+ // Determine the framework being used (priority: request > config > auto-detect > default)
541
+ const detectedFramework = frameworkOptions.framework ||
542
+ config.framework ||
543
+ (frameworkOptions.autoDetectFramework ? await detectProjectFramework(process.cwd()).catch(() => 'react') : 'react');
544
+ logger.log(`🎯 Framework detection: request=${frameworkOptions.framework}, config=${config.framework}, detected=${detectedFramework}`);
545
+ // Get the framework adapter for post-processing
546
+ const frameworkAdapter = getAdapter(detectedFramework);
547
+ // Only add React import for React framework
548
+ if (detectedFramework === 'react' && !fileContents.includes("import React from 'react';")) {
467
549
  fileContents = "import React from 'react';\n" + fileContents;
468
550
  }
469
551
  // Post-processing is now consolidated to run once on the final code
470
552
  let fixedFileContents = postProcessStory(fileContents, config.importPath);
553
+ // Apply framework-specific post-processing if adapter is available
554
+ if (frameworkAdapter) {
555
+ logger.log(`🔧 Applying ${detectedFramework} framework post-processing`);
556
+ fixedFileContents = frameworkAdapter.postProcess(fixedFileContents);
557
+ }
471
558
  // Generate title based on conversation context
472
559
  let aiTitle;
473
- if (isUpdate) {
474
- // For updates, try to keep the original title or modify it slightly
560
+ if (isActualUpdate && originalTitle) {
561
+ // For updates, preserve the original title
562
+ aiTitle = originalTitle;
563
+ logger.log('📝 Preserving original title for update:', aiTitle);
564
+ }
565
+ else if (isActualUpdate) {
566
+ // For updates without original title, try to keep the original title or modify it slightly
475
567
  const originalPrompt = conversation.find((msg) => msg.role === 'user')?.content || prompt;
476
- aiTitle = await getClaudeTitle(originalPrompt);
568
+ aiTitle = await getLLMTitle(originalPrompt);
477
569
  }
478
570
  else {
479
- aiTitle = await getClaudeTitle(prompt);
571
+ // For new stories, generate a new title
572
+ aiTitle = await getLLMTitle(prompt);
480
573
  }
481
574
  if (!aiTitle || aiTitle.length < 2) {
482
575
  // Fallback to cleaned prompt if Claude fails
@@ -486,20 +579,55 @@ export async function generateStoryFromPrompt(req, res) {
486
579
  const prettyPrompt = escapeTitleForTS(aiTitle);
487
580
  // Fix title with storyPrefix - handle both single-line and multi-line formats
488
581
  fixedFileContents = fixedFileContents.replace(/(const\s+meta\s*=\s*\{[\s\S]*?title:\s*["'])([^"']+)(["'])/, (match, p1, oldTitle, p3) => {
489
- const title = config.storyPrefix + prettyPrompt;
490
- return p1 + title + p3;
582
+ // Check if the title already has the prefix to avoid double prefixing
583
+ const titleToUse = prettyPrompt.startsWith(config.storyPrefix)
584
+ ? prettyPrompt
585
+ : config.storyPrefix + prettyPrompt;
586
+ return p1 + titleToUse + p3;
491
587
  });
492
588
  // Fallback: export default { title: "..." } format
493
589
  if (!fixedFileContents.includes(config.storyPrefix)) {
494
590
  fixedFileContents = fixedFileContents.replace(/(export\s+default\s*\{[\s\S]*?title:\s*["'])([^"']+)(["'])/, (match, p1, oldTitle, p3) => {
495
- const title = config.storyPrefix + prettyPrompt;
496
- return p1 + title + p3;
591
+ // Check if the title already has the prefix to avoid double prefixing
592
+ const titleToUse = prettyPrompt.startsWith(config.storyPrefix)
593
+ ? prettyPrompt
594
+ : config.storyPrefix + prettyPrompt;
595
+ return p1 + titleToUse + p3;
497
596
  });
498
597
  }
598
+ // FIX #5: Final validation after ALL post-processing
599
+ // This catches any syntax errors introduced by post-processing (e.g., buggy regex replacements)
600
+ const finalValidation = validateStoryCode(fixedFileContents, 'story.tsx', config);
601
+ if (!finalValidation.isValid) {
602
+ logger.log('⚠️ Post-processing introduced syntax errors:', finalValidation.errors);
603
+ // If we have fixed code from validation, use it
604
+ if (finalValidation.fixedCode) {
605
+ logger.log('✅ Auto-fixed post-processing errors');
606
+ fixedFileContents = finalValidation.fixedCode;
607
+ }
608
+ else {
609
+ // Post-processing broke the code and we can't fix it
610
+ // Return an error rather than serving broken code
611
+ console.error('Post-processing introduced unfixable syntax errors:', finalValidation.errors);
612
+ return res.status(500).json({
613
+ error: 'Story generation failed due to post-processing errors',
614
+ details: finalValidation.errors,
615
+ suggestion: 'This is a bug in Story UI. Please report this issue with your prompt.',
616
+ validation: {
617
+ hasWarnings: true,
618
+ errors: finalValidation.errors,
619
+ warnings: ['Post-processing introduced syntax errors that could not be automatically fixed']
620
+ }
621
+ });
622
+ }
623
+ }
624
+ else {
625
+ logger.log('✅ Final validation passed after post-processing');
626
+ }
499
627
  // Check if this is an update to an existing story
500
628
  // ONLY consider it an update if we're in the same conversation context
501
629
  let existingStory = null;
502
- if (isUpdate && fileName) {
630
+ if (isActualUpdate && fileName) {
503
631
  // When updating within a conversation, look for the story by fileName
504
632
  existingStory = storyTracker.findByTitle(aiTitle);
505
633
  if (existingStory && existingStory.fileName !== fileName) {
@@ -510,15 +638,23 @@ export async function generateStoryFromPrompt(req, res) {
510
638
  // Remove the automatic "find by prompt" logic that was preventing duplicates
511
639
  // Generate unique ID and filename
512
640
  let hash, finalFileName, storyId;
513
- let isActuallyUpdate = false;
514
- if (isUpdate && fileName) {
515
- // For conversation-based updates, use existing fileName and ID
641
+ if (isActualUpdate && (fileName || providedStoryId)) {
642
+ // For updates, preserve the existing fileName and ID
516
643
  finalFileName = fileName;
517
- // Extract hash from existing fileName if possible
518
- const hashMatch = fileName.match(/-([a-f0-9]{8})(?:\.stories\.tsx)?$/);
519
- hash = hashMatch ? hashMatch[1] : crypto.createHash('sha1').update(prompt).digest('hex').slice(0, 8);
520
- storyId = `story-${hash}`;
521
- isActuallyUpdate = true;
644
+ // Use provided storyId or extract from fileName
645
+ if (providedStoryId) {
646
+ storyId = providedStoryId;
647
+ // Extract hash from storyId
648
+ const hashMatch = providedStoryId.match(/^story-([a-f0-9]{8})$/);
649
+ hash = hashMatch ? hashMatch[1] : crypto.createHash('sha1').update(prompt).digest('hex').slice(0, 8);
650
+ }
651
+ else {
652
+ // Extract hash from existing fileName if possible
653
+ const hashMatch = fileName.match(/-([a-f0-9]{8})(?:\.stories\.tsx)?$/);
654
+ hash = hashMatch ? hashMatch[1] : crypto.createHash('sha1').update(prompt).digest('hex').slice(0, 8);
655
+ storyId = `story-${hash}`;
656
+ }
657
+ logger.log('📌 Preserving story identity for update:', { storyId, fileName: finalFileName });
522
658
  }
523
659
  else {
524
660
  // For new stories, ALWAYS generate new IDs with timestamp to ensure uniqueness
@@ -526,18 +662,18 @@ export async function generateStoryFromPrompt(req, res) {
526
662
  hash = crypto.createHash('sha1').update(prompt + timestamp).digest('hex').slice(0, 8);
527
663
  finalFileName = fileName || fileNameFromTitle(aiTitle, hash);
528
664
  storyId = `story-${hash}`;
529
- isActuallyUpdate = false;
665
+ logger.log('🆕 Creating new story:', { storyId, fileName: finalFileName });
530
666
  }
531
667
  if (isProduction) {
532
668
  // Production: Store in memory
533
669
  const generatedStory = {
534
670
  id: storyId,
535
671
  title: aiTitle,
536
- description: isActuallyUpdate ? `Updated: ${prompt}` : prompt,
672
+ description: isActualUpdate ? `Updated: ${prompt}` : prompt,
537
673
  content: fixedFileContents,
538
- createdAt: isActuallyUpdate ? (new Date()) : new Date(),
674
+ createdAt: isActualUpdate ? (new Date()) : new Date(),
539
675
  lastAccessed: new Date(),
540
- prompt: isActuallyUpdate ? conversation.map((msg) => `${msg.role}: ${msg.content}`).join('\n\n') : prompt,
676
+ prompt: isActualUpdate ? conversation.map((msg) => `${msg.role}: ${msg.content}`).join('\n\n') : prompt,
541
677
  components: extractComponentsFromContent(fixedFileContents)
542
678
  };
543
679
  storyService.storeStory(generatedStory);
@@ -554,7 +690,23 @@ export async function generateStoryFromPrompt(req, res) {
554
690
  storyTracker.registerStory(mapping);
555
691
  // Save to history
556
692
  historyManager.addVersion(finalFileName, prompt, fixedFileContents, parentVersionId);
557
- console.log(`Story ${isActuallyUpdate ? 'updated' : 'stored'} in memory: ${storyId}`);
693
+ logger.log(`Story ${isActualUpdate ? 'updated' : 'stored'} in memory: ${storyId}`);
694
+ // Track URL redirect if this is an update and the title changed
695
+ if (isActualUpdate && oldTitle && oldStoryUrl) {
696
+ // Extract the new title from the fixed content
697
+ const newTitleMatch = fixedFileContents.match(/title:\s*["']([^"']+)['"]/);
698
+ if (newTitleMatch) {
699
+ const newTitle = newTitleMatch[1];
700
+ // Remove the prefix to get clean title for URL
701
+ const cleanNewTitle = newTitle.replace(config.storyPrefix, '');
702
+ const cleanOldTitle = oldTitle.replace(config.storyPrefix, '');
703
+ const newStoryUrl = `/story/${cleanNewTitle.toLowerCase().replace(/[^a-z0-9]+/g, '-')}--primary`;
704
+ if (oldStoryUrl !== newStoryUrl) {
705
+ redirectService.addRedirect(oldStoryUrl, newStoryUrl, cleanOldTitle, cleanNewTitle, storyId);
706
+ logger.log(`🔀 Added redirect: ${oldStoryUrl} → ${newStoryUrl}`);
707
+ }
708
+ }
709
+ }
558
710
  res.json({
559
711
  success: true,
560
712
  fileName: finalFileName,
@@ -563,7 +715,7 @@ export async function generateStoryFromPrompt(req, res) {
563
715
  story: fileContents,
564
716
  environment: 'production',
565
717
  storage: 'in-memory',
566
- isUpdate: isActuallyUpdate,
718
+ isUpdate: isActualUpdate,
567
719
  validation: {
568
720
  hasWarnings: hasValidationWarnings,
569
721
  errors: validationResult?.errors || [],
@@ -591,16 +743,33 @@ export async function generateStoryFromPrompt(req, res) {
591
743
  storyTracker.registerStory(mapping);
592
744
  // Save to history
593
745
  historyManager.addVersion(finalFileName, prompt, fixedFileContents, parentVersionId);
594
- console.log(`Story ${isActuallyUpdate ? 'updated' : 'written'} to:`, outPath);
746
+ logger.log(`Story ${isActualUpdate ? 'updated' : 'written'} to:`, outPath);
747
+ // Track URL redirect if this is an update and the title changed
748
+ if (isActualUpdate && oldTitle && oldStoryUrl) {
749
+ // Extract the new title from the fixed content
750
+ const newTitleMatch = fixedFileContents.match(/title:\s*["']([^"']+)['"]/);
751
+ if (newTitleMatch) {
752
+ const newTitle = newTitleMatch[1];
753
+ // Remove the prefix to get clean title for URL
754
+ const cleanNewTitle = newTitle.replace(config.storyPrefix, '');
755
+ const cleanOldTitle = oldTitle.replace(config.storyPrefix, '');
756
+ const newStoryUrl = `/story/${cleanNewTitle.toLowerCase().replace(/[^a-z0-9]+/g, '-')}--primary`;
757
+ if (oldStoryUrl !== newStoryUrl) {
758
+ redirectService.addRedirect(oldStoryUrl, newStoryUrl, cleanOldTitle, cleanNewTitle, storyId);
759
+ logger.log(`🔀 Added redirect: ${oldStoryUrl} → ${newStoryUrl}`);
760
+ }
761
+ }
762
+ }
595
763
  res.json({
596
764
  success: true,
597
765
  fileName: finalFileName,
766
+ storyId,
598
767
  outPath,
599
768
  title: aiTitle,
600
769
  story: fileContents,
601
770
  environment: 'development',
602
771
  storage: 'file-system',
603
- isUpdate: isActuallyUpdate,
772
+ isUpdate: isActualUpdate,
604
773
  validation: {
605
774
  hasWarnings: hasValidationWarnings,
606
775
  errors: validationResult?.errors || [],
@@ -613,16 +782,6 @@ export async function generateStoryFromPrompt(req, res) {
613
782
  res.status(500).json({ error: err.message || 'Story generation failed' });
614
783
  }
615
784
  }
616
- /**
617
- * Fixes inline styles in the generated story content
618
- * Converts React camelCase style properties to kebab-case CSS properties
619
- */
620
- function fixInlineStyles(content) {
621
- // This function is now superseded by the validator and postProcessStory
622
- // but can be kept for other potential style cleanups if needed.
623
- // For now, the main logic is in the validator.
624
- return content;
625
- }
626
785
  /**
627
786
  * Extracts component names from story content
628
787
  */
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Streaming Story Generation with Two-Way Communication
3
+ *
4
+ * This endpoint provides real-time feedback during story generation via SSE.
5
+ * It enables the chat to show:
6
+ * 1. Intent preview - what the AI plans to do
7
+ * 2. Progress updates - step-by-step execution
8
+ * 3. Execution feedback - detailed completion with reasoning
9
+ */
10
+ import { Request, Response } from 'express';
11
+ export declare function generateStoryFromPromptStream(req: Request, res: Response): Promise<void>;
12
+ //# sourceMappingURL=generateStoryStream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generateStoryStream.d.ts","sourceRoot":"","sources":["../../../mcp-server/routes/generateStoryStream.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAqa5C,wBAAsB,6BAA6B,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,iBAge9E"}