@tpitre/story-ui 3.1.0 → 3.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 (38) hide show
  1. package/README.md +25 -72
  2. package/dist/cli/deploy.d.ts +0 -7
  3. package/dist/cli/deploy.d.ts.map +1 -1
  4. package/dist/cli/deploy.js +10 -421
  5. package/dist/cli/index.js +0 -29
  6. package/dist/mcp-server/index.js +20 -66
  7. package/dist/mcp-server/mcp-stdio-server.js +40 -110
  8. package/dist/mcp-server/routes/generateStory.d.ts.map +1 -1
  9. package/dist/mcp-server/routes/generateStory.js +46 -117
  10. package/dist/mcp-server/routes/generateStoryStream.d.ts.map +1 -1
  11. package/dist/mcp-server/routes/generateStoryStream.js +30 -72
  12. package/dist/mcp-server/routes/mcpRemote.d.ts +7 -3
  13. package/dist/mcp-server/routes/mcpRemote.d.ts.map +1 -1
  14. package/dist/mcp-server/routes/mcpRemote.js +353 -254
  15. package/dist/story-generator/generateStory.d.ts.map +1 -1
  16. package/dist/story-generator/generateStory.js +25 -0
  17. package/package.json +7 -7
  18. package/dist/mcp-server/routes/hybridStories.d.ts +0 -18
  19. package/dist/mcp-server/routes/hybridStories.d.ts.map +0 -1
  20. package/dist/mcp-server/routes/hybridStories.js +0 -214
  21. package/dist/mcp-server/routes/memoryStories.d.ts +0 -26
  22. package/dist/mcp-server/routes/memoryStories.d.ts.map +0 -1
  23. package/dist/mcp-server/routes/memoryStories.js +0 -147
  24. package/dist/mcp-server/routes/storySync.d.ts +0 -26
  25. package/dist/mcp-server/routes/storySync.d.ts.map +0 -1
  26. package/dist/mcp-server/routes/storySync.js +0 -147
  27. package/dist/mcp-server/sessionManager.d.ts +0 -50
  28. package/dist/mcp-server/sessionManager.d.ts.map +0 -1
  29. package/dist/mcp-server/sessionManager.js +0 -125
  30. package/dist/story-generator/inMemoryStoryService.d.ts +0 -89
  31. package/dist/story-generator/inMemoryStoryService.d.ts.map +0 -1
  32. package/dist/story-generator/inMemoryStoryService.js +0 -128
  33. package/dist/story-generator/productionGitignoreManager.d.ts +0 -91
  34. package/dist/story-generator/productionGitignoreManager.d.ts.map +0 -1
  35. package/dist/story-generator/productionGitignoreManager.js +0 -340
  36. package/dist/story-generator/storySync.d.ts +0 -68
  37. package/dist/story-generator/storySync.d.ts.map +0 -1
  38. package/dist/story-generator/storySync.js +0 -201
package/dist/cli/index.js CHANGED
@@ -4,8 +4,6 @@ import { spawn } from 'child_process';
4
4
  import fs from 'fs';
5
5
  import path from 'path';
6
6
  import { fileURLToPath } from 'url';
7
- import { setupProductionGitignore } from '../story-generator/productionGitignoreManager.js';
8
- import { createStoryUIConfig } from '../story-ui.config.js';
9
7
  import { setupCommand, cleanupDefaultStorybookComponents } from './setup.js';
10
8
  import { deployCommand } from './deploy.js';
11
9
  import net from 'net';
@@ -85,33 +83,6 @@ program
85
83
  console.log(`✅ Sample configuration generated: ${filename}`);
86
84
  }
87
85
  });
88
- program
89
- .command('setup-gitignore')
90
- .description('Set up .gitignore for Story UI generated stories')
91
- .option('-c, --config <path>', 'Path to Story UI config file', 'story-ui.config.js')
92
- .action(async (options) => {
93
- console.log('🔧 Setting up .gitignore for Story UI...');
94
- try {
95
- const configPath = path.resolve(process.cwd(), options.config);
96
- const configModule = await import(configPath);
97
- const userConfig = configModule.default;
98
- const fullConfig = createStoryUIConfig(userConfig);
99
- const manager = setupProductionGitignore(fullConfig);
100
- if (manager.isProductionMode()) {
101
- console.log('🌐 Production environment detected');
102
- console.log('✅ Gitignore validation completed');
103
- }
104
- else {
105
- console.log('🔧 Development environment - gitignore updated');
106
- }
107
- console.log('✅ Gitignore setup completed successfully!');
108
- }
109
- catch (error) {
110
- console.error('❌ Failed to set up gitignore:', error);
111
- console.log('💡 Make sure you have a valid story-ui.config.js file');
112
- process.exit(1);
113
- }
114
- });
115
86
  async function autoDetectAndCreateConfig() {
116
87
  const cwd = process.cwd();
117
88
  const config = {
@@ -13,11 +13,6 @@ import { getComponents, getProps } from './routes/components.js';
13
13
  import { claudeProxy } from './routes/claude.js';
14
14
  import { generateStoryFromPrompt } from './routes/generateStory.js';
15
15
  import { generateStoryFromPromptStream } from './routes/generateStoryStream.js';
16
- import { clearAllStories, getMemoryStats } from './routes/memoryStories.js';
17
- import { getAllStories, getStoryById, getStoryContent, deleteStory } from './routes/hybridStories.js';
18
- import { getSyncedStories, deleteSyncedStory, clearAllSyncedStories, syncChatHistory, validateChatSession, getSyncedStoryById } from './routes/storySync.js';
19
- import { setupProductionGitignore } from '../story-generator/productionGitignoreManager.js';
20
- import { getInMemoryStoryService } from '../story-generator/inMemoryStoryService.js';
21
16
  import { loadUserConfig } from '../story-generator/configLoader.js';
22
17
  import { loadConsiderations, considerationsToPrompt } from '../story-generator/considerationsLoader.js';
23
18
  import { DocumentationLoader } from '../story-generator/documentationLoader.js';
@@ -82,32 +77,14 @@ app.get('/mcp/frameworks/detect', detectCurrentFramework);
82
77
  app.get('/mcp/frameworks/:type', getFrameworkDetails);
83
78
  app.post('/mcp/frameworks/validate', validateStoryForFramework);
84
79
  app.post('/mcp/frameworks/post-process', postProcessStoryForFramework);
85
- // Hybrid story management routes (works in both dev and production)
86
- app.get('/mcp/stories', getAllStories);
87
- app.get('/mcp/stories/:id', getStoryById);
88
- app.get('/mcp/stories/:id/content', getStoryContent);
89
- app.delete('/mcp/stories/:id', deleteStory);
90
- app.delete('/mcp/stories', clearAllStories);
91
- app.get('/mcp/memory-stats', getMemoryStats);
92
- // Synchronized story management routes (works in both dev and production)
93
- app.get('/mcp/sync/stories', getSyncedStories);
94
- app.get('/mcp/sync/stories/:id', getSyncedStoryById);
95
- app.delete('/mcp/sync/stories/:id', deleteSyncedStory);
96
- app.delete('/mcp/sync/stories', clearAllSyncedStories);
97
- app.get('/mcp/sync/chat-history', syncChatHistory);
98
- app.get('/mcp/sync/validate/:id', validateChatSession);
80
+ // File-based story routes - stories are generated as .stories.tsx files
81
+ // Storybook discovers these automatically via its native file system watching
99
82
  // Proxy routes for frontend compatibility (maps /story-ui/ to /mcp/)
100
- app.get('/story-ui/stories', getAllStories);
101
- app.get('/story-ui/stories/:id', getStoryById);
102
- app.get('/story-ui/stories/:id/content', getStoryContent);
103
- app.delete('/story-ui/stories/:id', deleteStory);
104
- app.delete('/story-ui/stories', clearAllStories);
105
83
  app.post('/story-ui/generate', generateStoryFromPrompt);
106
84
  app.post('/story-ui/generate-stream', generateStoryFromPromptStream);
107
85
  app.post('/story-ui/claude', claudeProxy);
108
86
  app.get('/story-ui/components', getComponents);
109
87
  app.get('/story-ui/props', getProps);
110
- app.get('/story-ui/memory-stats', getMemoryStats);
111
88
  // Design system considerations endpoint - serves considerations for environment parity
112
89
  app.get('/story-ui/considerations', async (req, res) => {
113
90
  try {
@@ -168,41 +145,28 @@ app.get('/story-ui/frameworks/detect', detectCurrentFramework);
168
145
  app.get('/story-ui/frameworks/:type', getFrameworkDetails);
169
146
  app.post('/story-ui/frameworks/validate', validateStoryForFramework);
170
147
  app.post('/story-ui/frameworks/post-process', postProcessStoryForFramework);
171
- // Legacy delete route for backwards compatibility
148
+ // Delete story from file system
172
149
  app.post('/story-ui/delete', async (req, res) => {
173
150
  try {
174
151
  const { chatId, storyId } = req.body;
175
- const id = chatId || storyId; // Support both parameter names
152
+ const id = chatId || storyId;
176
153
  if (!id) {
177
154
  return res.status(400).json({ error: 'chatId or storyId is required' });
178
155
  }
179
156
  console.log(`🗑️ Attempting to delete story: ${id}`);
180
- // First try in-memory deletion (production mode)
181
- const storyService = getInMemoryStoryService(config);
182
- const inMemoryDeleted = storyService.deleteStory(id);
183
- if (inMemoryDeleted) {
184
- console.log(`✅ Deleted story from memory: ${id}`);
185
- return res.json({
186
- success: true,
187
- message: 'Story deleted successfully from memory'
188
- });
189
- }
190
- // If not found in memory, try file-system deletion (development mode)
191
- if (gitignoreManager && !gitignoreManager.isProductionMode()) {
192
- const storiesPath = config.generatedStoriesPath;
193
- console.log(`🔍 Searching for file-system story in: ${storiesPath}`);
194
- if (fs.existsSync(storiesPath)) {
195
- const files = fs.readdirSync(storiesPath);
196
- const matchingFile = files.find(file => file.includes(id) || file.replace('.stories.tsx', '') === id);
197
- if (matchingFile) {
198
- const filePath = path.join(storiesPath, matchingFile);
199
- fs.unlinkSync(filePath);
200
- console.log(`✅ Deleted story file: ${filePath}`);
201
- return res.json({
202
- success: true,
203
- message: 'Story deleted successfully from file system'
204
- });
205
- }
157
+ const storiesPath = config.generatedStoriesPath;
158
+ console.log(`🔍 Searching for story in: ${storiesPath}`);
159
+ if (fs.existsSync(storiesPath)) {
160
+ const files = fs.readdirSync(storiesPath);
161
+ const matchingFile = files.find(file => file.includes(id) || file.replace('.stories.tsx', '') === id);
162
+ if (matchingFile) {
163
+ const filePath = path.join(storiesPath, matchingFile);
164
+ fs.unlinkSync(filePath);
165
+ console.log(`✅ Deleted story file: ${filePath}`);
166
+ return res.json({
167
+ success: true,
168
+ message: 'Story deleted successfully'
169
+ });
206
170
  }
207
171
  }
208
172
  console.log(`❌ Story not found: ${id}`);
@@ -212,17 +176,10 @@ app.post('/story-ui/delete', async (req, res) => {
212
176
  });
213
177
  }
214
178
  catch (error) {
215
- console.error('Error in legacy delete route:', error);
179
+ console.error('Error deleting story:', error);
216
180
  res.status(500).json({ error: 'Failed to delete story' });
217
181
  }
218
182
  });
219
- // Synchronized story proxy routes
220
- app.get('/story-ui/sync/stories', getSyncedStories);
221
- app.get('/story-ui/sync/stories/:id', getSyncedStoryById);
222
- app.delete('/story-ui/sync/stories/:id', deleteSyncedStory);
223
- app.delete('/story-ui/sync/stories', clearAllSyncedStories);
224
- app.get('/story-ui/sync/chat-history', syncChatHistory);
225
- app.get('/story-ui/sync/validate/:id', validateChatSession);
226
183
  // MCP Remote HTTP transport routes (for Claude Desktop remote connections)
227
184
  // Provides Streamable HTTP and legacy SSE endpoints for remote MCP access
228
185
  app.use('/mcp-remote', mcpRemoteRouter);
@@ -237,18 +194,15 @@ app.get('/story-ui/redirects.js', (req, res) => {
237
194
  res.set('Content-Type', 'application/javascript');
238
195
  res.send(redirectService.getRedirectScript());
239
196
  });
240
- // Set up production-ready gitignore and directory structure on startup
197
+ // Load user config and initialize services
241
198
  const config = loadUserConfig();
242
- const gitignoreManager = setupProductionGitignore(config);
243
- const storyService = getInMemoryStoryService(config);
244
199
  // Initialize URL redirect service
245
200
  const redirectService = new UrlRedirectService(process.cwd());
246
201
  const PORT = parseInt(process.env.PORT || '4001', 10);
247
202
  // Start server
248
203
  app.listen(PORT, () => {
249
204
  console.log(`MCP server running on port ${PORT}`);
250
- console.log(`Environment: ${gitignoreManager.isProductionMode() ? 'Production' : 'Development'}`);
251
- console.log(`Story generation: ${gitignoreManager.isProductionMode() ? 'In-memory' : 'File-system'}`);
205
+ console.log(`Stories will be generated to: ${config.generatedStoriesPath}`);
252
206
  }).on('error', (err) => {
253
207
  if (err.code === 'EADDRINUSE') {
254
208
  console.error(`\n❌ Port ${PORT} is already in use!`);
@@ -5,12 +5,9 @@ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextpro
5
5
  import fetch from 'node-fetch';
6
6
  import { loadUserConfig } from '../story-generator/configLoader.js';
7
7
  import { EnhancedComponentDiscovery } from '../story-generator/enhancedComponentDiscovery.js';
8
- import { getInMemoryStoryService } from '../story-generator/inMemoryStoryService.js';
9
- import { SessionManager } from './sessionManager.js';
10
8
  import dotenv from 'dotenv';
11
9
  import path from 'path';
12
10
  import fs from 'fs';
13
- import crypto from 'crypto';
14
11
  import { fileURLToPath } from 'url';
15
12
  // Get package version dynamically
16
13
  const __filename_mcp = fileURLToPath(import.meta.url);
@@ -33,11 +30,6 @@ const HTTP_PORT = process.env.VITE_STORY_UI_PORT || process.env.STORY_UI_HTTP_PO
33
30
  const HTTP_BASE_URL = `http://localhost:${HTTP_PORT}`;
34
31
  // Initialize configuration
35
32
  const config = loadUserConfig();
36
- const storyService = getInMemoryStoryService(config);
37
- const sessionManager = SessionManager.getInstance();
38
- // Generate a session ID for this MCP connection
39
- const sessionId = crypto.randomBytes(16).toString('hex');
40
- console.error(`[MCP] Session ID: ${sessionId}`);
41
33
  // Create MCP server instance
42
34
  const server = new Server({
43
35
  name: "story-ui",
@@ -191,15 +183,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
191
183
  const result = await response.json();
192
184
  // Debug log to see what we're getting
193
185
  console.error('Story generation result:', JSON.stringify(result, null, 2));
194
- // Track the story in session
195
- if (result.storyId && result.fileName && result.title) {
196
- sessionManager.trackStory(sessionId, {
197
- id: result.storyId,
198
- fileName: result.fileName,
199
- title: result.title,
200
- prompt: prompt
201
- });
202
- }
203
186
  return {
204
187
  content: [{
205
188
  type: "text",
@@ -243,15 +226,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
243
226
  }
244
227
  case "list-stories": {
245
228
  try {
246
- // Get session stories
247
- const sessionStories = sessionManager.getSessionStories(sessionId);
248
- // Also try to get file system stories
249
- let fileStories = [];
229
+ // Get stories from file system
230
+ const stories = [];
250
231
  if (config.generatedStoriesPath && fs.existsSync(config.generatedStoriesPath)) {
251
232
  const files = fs.readdirSync(config.generatedStoriesPath);
252
- fileStories = files
253
- .filter(file => file.endsWith('.stories.tsx'))
254
- .map(file => {
233
+ files
234
+ .filter((file) => file.endsWith('.stories.tsx'))
235
+ .forEach((file) => {
255
236
  const hash = file.match(/-([a-f0-9]{8})\.stories\.tsx$/)?.[1] || '';
256
237
  const storyId = hash ? `story-${hash}` : file.replace('.stories.tsx', '');
257
238
  // Try to read title from file
@@ -267,16 +248,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
267
248
  catch (e) {
268
249
  // Use filename as fallback
269
250
  }
270
- return {
271
- id: storyId,
272
- fileName: file,
273
- title,
274
- source: 'file-system',
275
- isInSession: sessionStories.some(s => s.id === storyId)
276
- };
251
+ stories.push({ id: storyId, fileName: file, title });
277
252
  });
278
253
  }
279
- if (sessionStories.length === 0 && fileStories.length === 0) {
254
+ if (stories.length === 0) {
280
255
  return {
281
256
  content: [{
282
257
  type: "text",
@@ -284,35 +259,16 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
284
259
  }]
285
260
  };
286
261
  }
287
- let responseText = '';
288
- // Show session stories first
289
- if (sessionStories.length > 0) {
290
- responseText += `**Stories in current session:**\n`;
291
- const currentStory = sessionManager.getCurrentStory(sessionId);
292
- sessionStories.forEach(story => {
293
- const isCurrent = currentStory?.id === story.id;
294
- responseText += `\n${isCurrent ? '→ ' : ' '}${story.title}\n`;
295
- responseText += ` ID: ${story.id}\n`;
296
- responseText += ` File: ${story.fileName}\n`;
297
- if (isCurrent) {
298
- responseText += ` (Currently discussing this story)\n`;
299
- }
300
- });
301
- }
302
- // Show other available stories
303
- const nonSessionFiles = fileStories.filter(f => !f.isInSession);
304
- if (nonSessionFiles.length > 0) {
305
- responseText += `\n\n**Other available stories:**\n`;
306
- nonSessionFiles.forEach(story => {
307
- responseText += `\n- ${story.title}\n`;
308
- responseText += ` ID: ${story.id}\n`;
309
- responseText += ` File: ${story.fileName}\n`;
310
- });
311
- }
262
+ let responseText = `**Available stories (${stories.length}):**\n`;
263
+ stories.forEach(story => {
264
+ responseText += `\n- ${story.title}\n`;
265
+ responseText += ` ID: ${story.id}\n`;
266
+ responseText += ` File: ${story.fileName}\n`;
267
+ });
312
268
  responseText += `\n\n**Tips:**\n`;
313
269
  responseText += `- To update a story, just describe what changes you want\n`;
314
- responseText += `- I'll automatically work with the most recent story or find the right one based on context\n`;
315
- responseText += `- You can also specify a story ID directly if needed`;
270
+ responseText += `- Specify a story ID if you want to update a specific story\n`;
271
+ responseText += `- Stories are stored in: ${config.generatedStoriesPath}`;
316
272
  return {
317
273
  content: [{
318
274
  type: "text",
@@ -384,17 +340,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
384
340
  const filePath = path.join(config.generatedStoriesPath, matchingFile);
385
341
  fs.unlinkSync(filePath);
386
342
  console.error(`[MCP] Deleted story file: ${filePath}`);
387
- // Also remove from session
388
- const sessionStories = sessionManager.getSessionStories(sessionId);
389
- const storyInSession = sessionStories.find(s => s.id === storyId);
390
- if (storyInSession) {
391
- // Note: SessionManager doesn't have a removeStory method yet
392
- // For now, just clear current if it matches
393
- const current = sessionManager.getCurrentStory(sessionId);
394
- if (current?.id === storyId) {
395
- sessionManager.setCurrentStory(sessionId, '');
396
- }
397
- }
398
343
  return {
399
344
  content: [{
400
345
  type: "text",
@@ -454,40 +399,34 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
454
399
  case "update-story": {
455
400
  let { storyId, prompt } = args;
456
401
  try {
457
- // If no storyId provided, try to find the right story
402
+ // If no storyId provided, find the most recently modified story file
458
403
  if (!storyId) {
459
- // First check current story in session
460
- const currentStory = sessionManager.getCurrentStory(sessionId);
461
- if (currentStory) {
462
- storyId = currentStory.id;
463
- console.error(`[MCP] Using current story: ${currentStory.title} (${storyId})`);
464
- }
465
- else {
466
- // Try to find by context
467
- const contextStory = sessionManager.findStoryByContext(sessionId, prompt);
468
- if (contextStory) {
469
- storyId = contextStory.id;
470
- console.error(`[MCP] Found story by context: ${contextStory.title} (${storyId})`);
471
- }
472
- else {
473
- // Use the most recent story
474
- const sessionStories = sessionManager.getSessionStories(sessionId);
475
- if (sessionStories.length > 0) {
476
- const recentStory = sessionStories[sessionStories.length - 1];
477
- storyId = recentStory.id;
478
- console.error(`[MCP] Using most recent story: ${recentStory.title} (${storyId})`);
479
- }
480
- else {
481
- return {
482
- content: [{
483
- type: "text",
484
- text: "No story found to update. Please generate a story first or specify which story you'd like to update."
485
- }],
486
- isError: true
487
- };
488
- }
404
+ if (config.generatedStoriesPath && fs.existsSync(config.generatedStoriesPath)) {
405
+ const files = fs.readdirSync(config.generatedStoriesPath)
406
+ .filter((file) => file.endsWith('.stories.tsx'))
407
+ .map((file) => {
408
+ const filePath = path.join(config.generatedStoriesPath, file);
409
+ const stats = fs.statSync(filePath);
410
+ return { file, mtime: stats.mtime.getTime() };
411
+ })
412
+ .sort((a, b) => b.mtime - a.mtime);
413
+ if (files.length > 0) {
414
+ // Use the most recently modified story
415
+ const mostRecent = files[0].file;
416
+ const hashMatch = mostRecent.match(/-([a-f0-9]{8})\.stories\.tsx$/);
417
+ storyId = hashMatch ? `story-${hashMatch[1]}` : mostRecent.replace('.stories.tsx', '');
418
+ console.error(`[MCP] Using most recent story: ${mostRecent} (${storyId})`);
489
419
  }
490
420
  }
421
+ if (!storyId) {
422
+ return {
423
+ content: [{
424
+ type: "text",
425
+ text: "No story found to update. Please generate a story first or specify which story you'd like to update."
426
+ }],
427
+ isError: true
428
+ };
429
+ }
491
430
  }
492
431
  // Try to get story content directly from file system first
493
432
  let existingCode = '';
@@ -572,15 +511,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
572
511
  const result = await response.json();
573
512
  // Debug log to see what we're getting
574
513
  console.error('Story update result:', JSON.stringify(result, null, 2));
575
- // Update session tracking with preserved metadata
576
- if (result.storyId && result.fileName && result.title) {
577
- sessionManager.trackStory(sessionId, {
578
- id: result.storyId,
579
- fileName: result.fileName,
580
- title: result.title,
581
- prompt: prompt
582
- });
583
- }
584
514
  return {
585
515
  content: [{
586
516
  type: "text",
@@ -1 +1 @@
1
- {"version":3,"file":"generateStory.d.ts","sourceRoot":"","sources":["../../../mcp-server/routes/generateStory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAuZ5C,wBAAsB,uBAAuB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,2DAigBxE"}
1
+ {"version":3,"file":"generateStory.d.ts","sourceRoot":"","sources":["../../../mcp-server/routes/generateStory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAqZ5C,wBAAsB,uBAAuB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,2DAkbxE"}
@@ -4,8 +4,6 @@ import * as path from 'path';
4
4
  import { buildClaudePrompt as buildFlexiblePrompt, buildFrameworkAwarePrompt, detectProjectFramework, } from '../../story-generator/promptGenerator.js';
5
5
  import { getAdapter } from '../../story-generator/framework-adapters/index.js';
6
6
  import { loadUserConfig, validateConfig } from '../../story-generator/configLoader.js';
7
- import { setupProductionGitignore } from '../../story-generator/productionGitignoreManager.js';
8
- import { getInMemoryStoryService } from '../../story-generator/inMemoryStoryService.js';
9
7
  import { extractAndValidateCodeBlock, createFallbackStory, validateStoryCode } from '../../story-generator/validateStory.js';
10
8
  import { isBlacklistedComponent, isBlacklistedIcon, getBlacklistErrorMessage, ICON_CORRECTIONS } from '../../story-generator/componentBlacklist.js';
11
9
  import { StoryTracker } from '../../story-generator/storyTracker.js';
@@ -375,17 +373,12 @@ export async function generateStoryFromPrompt(req, res) {
375
373
  });
376
374
  }
377
375
  }
378
- // Set up production-ready environment
379
- const gitignoreManager = setupProductionGitignore(config);
380
- const storyService = getInMemoryStoryService(config);
381
- const isProduction = gitignoreManager.isProductionMode();
382
376
  // Initialize story tracker for managing updates vs new creations
383
377
  const storyTracker = new StoryTracker(config);
384
378
  // Initialize history manager - use the current working directory
385
379
  const historyManager = new StoryHistoryManager(process.cwd());
386
380
  // 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);
381
+ const redirectDir = path.dirname(config.generatedStoriesPath);
389
382
  const redirectService = new UrlRedirectService(redirectDir);
390
383
  // Check if this is an update to an existing story
391
384
  // Use the explicit isUpdate flag from request, or fallback to old logic
@@ -664,119 +657,55 @@ export async function generateStoryFromPrompt(req, res) {
664
657
  storyId = `story-${hash}`;
665
658
  logger.log('🆕 Creating new story:', { storyId, fileName: finalFileName });
666
659
  }
667
- if (isProduction) {
668
- // Production: Store in memory
669
- const generatedStory = {
670
- id: storyId,
671
- title: aiTitle,
672
- description: isActualUpdate ? `Updated: ${prompt}` : prompt,
673
- content: fixedFileContents,
674
- createdAt: isActualUpdate ? (new Date()) : new Date(),
675
- lastAccessed: new Date(),
676
- prompt: isActualUpdate ? conversation.map((msg) => `${msg.role}: ${msg.content}`).join('\n\n') : prompt,
677
- components: extractComponentsFromContent(fixedFileContents)
678
- };
679
- storyService.storeStory(generatedStory);
680
- // Register with story tracker
681
- const mapping = {
682
- title: aiTitle,
683
- fileName: finalFileName,
684
- storyId,
685
- hash,
686
- createdAt: new Date().toISOString(),
687
- updatedAt: new Date().toISOString(),
688
- prompt
689
- };
690
- storyTracker.registerStory(mapping);
691
- // Save to history
692
- historyManager.addVersion(finalFileName, prompt, fixedFileContents, parentVersionId);
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
- }
660
+ // Write story to file system
661
+ const outPath = generateStory({
662
+ fileContents: fixedFileContents,
663
+ fileName: finalFileName,
664
+ config: config
665
+ });
666
+ // Register with story tracker
667
+ const mapping = {
668
+ title: aiTitle,
669
+ fileName: finalFileName,
670
+ storyId,
671
+ hash,
672
+ createdAt: new Date().toISOString(),
673
+ updatedAt: new Date().toISOString(),
674
+ prompt
675
+ };
676
+ storyTracker.registerStory(mapping);
677
+ // Save to history
678
+ historyManager.addVersion(finalFileName, prompt, fixedFileContents, parentVersionId);
679
+ logger.log(`Story ${isActualUpdate ? 'updated' : 'written'} to:`, outPath);
680
+ // Track URL redirect if this is an update and the title changed
681
+ if (isActualUpdate && oldTitle && oldStoryUrl) {
682
+ const newTitleMatch = fixedFileContents.match(/title:\s*["']([^"']+)['"]/);
683
+ if (newTitleMatch) {
684
+ const newTitle = newTitleMatch[1];
685
+ const cleanNewTitle = newTitle.replace(config.storyPrefix, '');
686
+ const cleanOldTitle = oldTitle.replace(config.storyPrefix, '');
687
+ const newStoryUrl = `/story/${cleanNewTitle.toLowerCase().replace(/[^a-z0-9]+/g, '-')}--primary`;
688
+ if (oldStoryUrl !== newStoryUrl) {
689
+ redirectService.addRedirect(oldStoryUrl, newStoryUrl, cleanOldTitle, cleanNewTitle, storyId);
690
+ logger.log(`🔀 Added redirect: ${oldStoryUrl} → ${newStoryUrl}`);
708
691
  }
709
692
  }
710
- res.json({
711
- success: true,
712
- fileName: finalFileName,
713
- storyId,
714
- title: aiTitle,
715
- story: fileContents,
716
- environment: 'production',
717
- storage: 'in-memory',
718
- isUpdate: isActualUpdate,
719
- validation: {
720
- hasWarnings: hasValidationWarnings,
721
- errors: validationResult?.errors || [],
722
- warnings: validationResult?.warnings || []
723
- }
724
- });
725
693
  }
726
- else {
727
- // Development: Write to file system
728
- const outPath = generateStory({
729
- fileContents: fixedFileContents,
730
- fileName: finalFileName,
731
- config: config
732
- });
733
- // Register with story tracker
734
- const mapping = {
735
- title: aiTitle,
736
- fileName: finalFileName,
737
- storyId,
738
- hash,
739
- createdAt: new Date().toISOString(),
740
- updatedAt: new Date().toISOString(),
741
- prompt
742
- };
743
- storyTracker.registerStory(mapping);
744
- // Save to history
745
- historyManager.addVersion(finalFileName, prompt, fixedFileContents, parentVersionId);
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
- }
694
+ res.json({
695
+ success: true,
696
+ fileName: finalFileName,
697
+ storyId,
698
+ outPath,
699
+ title: aiTitle,
700
+ story: fileContents,
701
+ storage: 'file-system',
702
+ isUpdate: isActualUpdate,
703
+ validation: {
704
+ hasWarnings: hasValidationWarnings,
705
+ errors: validationResult?.errors || [],
706
+ warnings: validationResult?.warnings || []
762
707
  }
763
- res.json({
764
- success: true,
765
- fileName: finalFileName,
766
- storyId,
767
- outPath,
768
- title: aiTitle,
769
- story: fileContents,
770
- environment: 'development',
771
- storage: 'file-system',
772
- isUpdate: isActualUpdate,
773
- validation: {
774
- hasWarnings: hasValidationWarnings,
775
- errors: validationResult?.errors || [],
776
- warnings: validationResult?.warnings || []
777
- }
778
- });
779
- }
708
+ });
780
709
  }
781
710
  catch (err) {
782
711
  res.status(500).json({ error: err.message || 'Story generation failed' });
@@ -1 +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"}
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;AAma5C,wBAAsB,6BAA6B,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,iBAob9E"}