@tpitre/story-ui 3.2.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 (43) hide show
  1. package/dist/cli/index.js +0 -29
  2. package/dist/mcp-server/index.js +20 -65
  3. package/dist/mcp-server/mcp-stdio-server.js +40 -109
  4. package/dist/mcp-server/routes/generateStory.d.ts.map +1 -1
  5. package/dist/mcp-server/routes/generateStory.js +46 -117
  6. package/dist/mcp-server/routes/generateStoryStream.d.ts.map +1 -1
  7. package/dist/mcp-server/routes/generateStoryStream.js +30 -72
  8. package/dist/mcp-server/routes/mcpRemote.d.ts +7 -3
  9. package/dist/mcp-server/routes/mcpRemote.d.ts.map +1 -1
  10. package/dist/mcp-server/routes/mcpRemote.js +353 -254
  11. package/dist/story-generator/generateStory.d.ts.map +1 -1
  12. package/dist/story-generator/generateStory.js +25 -0
  13. package/package.json +2 -2
  14. package/dist/mcp-server/routes/hybridStories.d.ts +0 -18
  15. package/dist/mcp-server/routes/hybridStories.d.ts.map +0 -1
  16. package/dist/mcp-server/routes/hybridStories.js +0 -216
  17. package/dist/mcp-server/routes/memoryStories.d.ts +0 -26
  18. package/dist/mcp-server/routes/memoryStories.d.ts.map +0 -1
  19. package/dist/mcp-server/routes/memoryStories.js +0 -158
  20. package/dist/mcp-server/routes/storySync.d.ts +0 -26
  21. package/dist/mcp-server/routes/storySync.d.ts.map +0 -1
  22. package/dist/mcp-server/routes/storySync.js +0 -147
  23. package/dist/mcp-server/sessionManager.d.ts +0 -50
  24. package/dist/mcp-server/sessionManager.d.ts.map +0 -1
  25. package/dist/mcp-server/sessionManager.js +0 -125
  26. package/dist/story-generator/inMemoryStoryService.d.ts +0 -89
  27. package/dist/story-generator/inMemoryStoryService.d.ts.map +0 -1
  28. package/dist/story-generator/inMemoryStoryService.js +0 -128
  29. package/dist/story-generator/postgresStoryService.d.ts +0 -56
  30. package/dist/story-generator/postgresStoryService.d.ts.map +0 -1
  31. package/dist/story-generator/postgresStoryService.js +0 -240
  32. package/dist/story-generator/productionGitignoreManager.d.ts +0 -91
  33. package/dist/story-generator/productionGitignoreManager.d.ts.map +0 -1
  34. package/dist/story-generator/productionGitignoreManager.js +0 -340
  35. package/dist/story-generator/storyServiceFactory.d.ts +0 -22
  36. package/dist/story-generator/storyServiceFactory.d.ts.map +0 -1
  37. package/dist/story-generator/storyServiceFactory.js +0 -97
  38. package/dist/story-generator/storyServiceInterface.d.ts +0 -85
  39. package/dist/story-generator/storyServiceInterface.d.ts.map +0 -1
  40. package/dist/story-generator/storyServiceInterface.js +0 -5
  41. package/dist/story-generator/storySync.d.ts +0 -68
  42. package/dist/story-generator/storySync.d.ts.map +0 -1
  43. 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 { getStoryService, getStorageType } from '../story-generator/storyServiceFactory.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 storage service deletion (production mode)
181
- const storyService = await getStoryService(config);
182
- const serviceDeleted = await storyService.deleteStory(id);
183
- if (serviceDeleted) {
184
- console.log(`✅ Deleted story from ${getStorageType()}: ${id}`);
185
- return res.json({
186
- success: true,
187
- message: `Story deleted successfully from ${getStorageType()}`
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,17 +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
199
  // Initialize URL redirect service
244
200
  const redirectService = new UrlRedirectService(process.cwd());
245
201
  const PORT = parseInt(process.env.PORT || '4001', 10);
246
202
  // Start server
247
203
  app.listen(PORT, () => {
248
204
  console.log(`MCP server running on port ${PORT}`);
249
- console.log(`Environment: ${gitignoreManager.isProductionMode() ? 'Production' : 'Development'}`);
250
- console.log(`Story generation: ${gitignoreManager.isProductionMode() ? 'In-memory' : 'File-system'}`);
205
+ console.log(`Stories will be generated to: ${config.generatedStoriesPath}`);
251
206
  }).on('error', (err) => {
252
207
  if (err.code === 'EADDRINUSE') {
253
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
- // Story service is now handled by HTTP server routes
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,10 +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 sessionManager = SessionManager.getInstance();
37
- // Generate a session ID for this MCP connection
38
- const sessionId = crypto.randomBytes(16).toString('hex');
39
- console.error(`[MCP] Session ID: ${sessionId}`);
40
33
  // Create MCP server instance
41
34
  const server = new Server({
42
35
  name: "story-ui",
@@ -190,15 +183,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
190
183
  const result = await response.json();
191
184
  // Debug log to see what we're getting
192
185
  console.error('Story generation result:', JSON.stringify(result, null, 2));
193
- // Track the story in session
194
- if (result.storyId && result.fileName && result.title) {
195
- sessionManager.trackStory(sessionId, {
196
- id: result.storyId,
197
- fileName: result.fileName,
198
- title: result.title,
199
- prompt: prompt
200
- });
201
- }
202
186
  return {
203
187
  content: [{
204
188
  type: "text",
@@ -242,15 +226,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
242
226
  }
243
227
  case "list-stories": {
244
228
  try {
245
- // Get session stories
246
- const sessionStories = sessionManager.getSessionStories(sessionId);
247
- // Also try to get file system stories
248
- let fileStories = [];
229
+ // Get stories from file system
230
+ const stories = [];
249
231
  if (config.generatedStoriesPath && fs.existsSync(config.generatedStoriesPath)) {
250
232
  const files = fs.readdirSync(config.generatedStoriesPath);
251
- fileStories = files
252
- .filter(file => file.endsWith('.stories.tsx'))
253
- .map(file => {
233
+ files
234
+ .filter((file) => file.endsWith('.stories.tsx'))
235
+ .forEach((file) => {
254
236
  const hash = file.match(/-([a-f0-9]{8})\.stories\.tsx$/)?.[1] || '';
255
237
  const storyId = hash ? `story-${hash}` : file.replace('.stories.tsx', '');
256
238
  // Try to read title from file
@@ -266,16 +248,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
266
248
  catch (e) {
267
249
  // Use filename as fallback
268
250
  }
269
- return {
270
- id: storyId,
271
- fileName: file,
272
- title,
273
- source: 'file-system',
274
- isInSession: sessionStories.some(s => s.id === storyId)
275
- };
251
+ stories.push({ id: storyId, fileName: file, title });
276
252
  });
277
253
  }
278
- if (sessionStories.length === 0 && fileStories.length === 0) {
254
+ if (stories.length === 0) {
279
255
  return {
280
256
  content: [{
281
257
  type: "text",
@@ -283,35 +259,16 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
283
259
  }]
284
260
  };
285
261
  }
286
- let responseText = '';
287
- // Show session stories first
288
- if (sessionStories.length > 0) {
289
- responseText += `**Stories in current session:**\n`;
290
- const currentStory = sessionManager.getCurrentStory(sessionId);
291
- sessionStories.forEach(story => {
292
- const isCurrent = currentStory?.id === story.id;
293
- responseText += `\n${isCurrent ? '→ ' : ' '}${story.title}\n`;
294
- responseText += ` ID: ${story.id}\n`;
295
- responseText += ` File: ${story.fileName}\n`;
296
- if (isCurrent) {
297
- responseText += ` (Currently discussing this story)\n`;
298
- }
299
- });
300
- }
301
- // Show other available stories
302
- const nonSessionFiles = fileStories.filter(f => !f.isInSession);
303
- if (nonSessionFiles.length > 0) {
304
- responseText += `\n\n**Other available stories:**\n`;
305
- nonSessionFiles.forEach(story => {
306
- responseText += `\n- ${story.title}\n`;
307
- responseText += ` ID: ${story.id}\n`;
308
- responseText += ` File: ${story.fileName}\n`;
309
- });
310
- }
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
+ });
311
268
  responseText += `\n\n**Tips:**\n`;
312
269
  responseText += `- To update a story, just describe what changes you want\n`;
313
- responseText += `- I'll automatically work with the most recent story or find the right one based on context\n`;
314
- 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}`;
315
272
  return {
316
273
  content: [{
317
274
  type: "text",
@@ -383,17 +340,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
383
340
  const filePath = path.join(config.generatedStoriesPath, matchingFile);
384
341
  fs.unlinkSync(filePath);
385
342
  console.error(`[MCP] Deleted story file: ${filePath}`);
386
- // Also remove from session
387
- const sessionStories = sessionManager.getSessionStories(sessionId);
388
- const storyInSession = sessionStories.find(s => s.id === storyId);
389
- if (storyInSession) {
390
- // Note: SessionManager doesn't have a removeStory method yet
391
- // For now, just clear current if it matches
392
- const current = sessionManager.getCurrentStory(sessionId);
393
- if (current?.id === storyId) {
394
- sessionManager.setCurrentStory(sessionId, '');
395
- }
396
- }
397
343
  return {
398
344
  content: [{
399
345
  type: "text",
@@ -453,40 +399,34 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
453
399
  case "update-story": {
454
400
  let { storyId, prompt } = args;
455
401
  try {
456
- // If no storyId provided, try to find the right story
402
+ // If no storyId provided, find the most recently modified story file
457
403
  if (!storyId) {
458
- // First check current story in session
459
- const currentStory = sessionManager.getCurrentStory(sessionId);
460
- if (currentStory) {
461
- storyId = currentStory.id;
462
- console.error(`[MCP] Using current story: ${currentStory.title} (${storyId})`);
463
- }
464
- else {
465
- // Try to find by context
466
- const contextStory = sessionManager.findStoryByContext(sessionId, prompt);
467
- if (contextStory) {
468
- storyId = contextStory.id;
469
- console.error(`[MCP] Found story by context: ${contextStory.title} (${storyId})`);
470
- }
471
- else {
472
- // Use the most recent story
473
- const sessionStories = sessionManager.getSessionStories(sessionId);
474
- if (sessionStories.length > 0) {
475
- const recentStory = sessionStories[sessionStories.length - 1];
476
- storyId = recentStory.id;
477
- console.error(`[MCP] Using most recent story: ${recentStory.title} (${storyId})`);
478
- }
479
- else {
480
- return {
481
- content: [{
482
- type: "text",
483
- text: "No story found to update. Please generate a story first or specify which story you'd like to update."
484
- }],
485
- isError: true
486
- };
487
- }
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})`);
488
419
  }
489
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
+ }
490
430
  }
491
431
  // Try to get story content directly from file system first
492
432
  let existingCode = '';
@@ -571,15 +511,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
571
511
  const result = await response.json();
572
512
  // Debug log to see what we're getting
573
513
  console.error('Story update result:', JSON.stringify(result, null, 2));
574
- // Update session tracking with preserved metadata
575
- if (result.storyId && result.fileName && result.title) {
576
- sessionManager.trackStory(sessionId, {
577
- id: result.storyId,
578
- fileName: result.fileName,
579
- title: result.title,
580
- prompt: prompt
581
- });
582
- }
583
514
  return {
584
515
  content: [{
585
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;AAwZ5C,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 { getStoryService } from '../../story-generator/storyServiceFactory.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 = await getStoryService(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
- await 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;AAsa5C,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"}