@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.
- package/dist/cli/index.js +0 -29
- package/dist/mcp-server/index.js +20 -65
- package/dist/mcp-server/mcp-stdio-server.js +40 -109
- package/dist/mcp-server/routes/generateStory.d.ts.map +1 -1
- package/dist/mcp-server/routes/generateStory.js +46 -117
- package/dist/mcp-server/routes/generateStoryStream.d.ts.map +1 -1
- package/dist/mcp-server/routes/generateStoryStream.js +30 -72
- package/dist/mcp-server/routes/mcpRemote.d.ts +7 -3
- package/dist/mcp-server/routes/mcpRemote.d.ts.map +1 -1
- package/dist/mcp-server/routes/mcpRemote.js +353 -254
- package/dist/story-generator/generateStory.d.ts.map +1 -1
- package/dist/story-generator/generateStory.js +25 -0
- package/package.json +2 -2
- package/dist/mcp-server/routes/hybridStories.d.ts +0 -18
- package/dist/mcp-server/routes/hybridStories.d.ts.map +0 -1
- package/dist/mcp-server/routes/hybridStories.js +0 -216
- package/dist/mcp-server/routes/memoryStories.d.ts +0 -26
- package/dist/mcp-server/routes/memoryStories.d.ts.map +0 -1
- package/dist/mcp-server/routes/memoryStories.js +0 -158
- package/dist/mcp-server/routes/storySync.d.ts +0 -26
- package/dist/mcp-server/routes/storySync.d.ts.map +0 -1
- package/dist/mcp-server/routes/storySync.js +0 -147
- package/dist/mcp-server/sessionManager.d.ts +0 -50
- package/dist/mcp-server/sessionManager.d.ts.map +0 -1
- package/dist/mcp-server/sessionManager.js +0 -125
- package/dist/story-generator/inMemoryStoryService.d.ts +0 -89
- package/dist/story-generator/inMemoryStoryService.d.ts.map +0 -1
- package/dist/story-generator/inMemoryStoryService.js +0 -128
- package/dist/story-generator/postgresStoryService.d.ts +0 -56
- package/dist/story-generator/postgresStoryService.d.ts.map +0 -1
- package/dist/story-generator/postgresStoryService.js +0 -240
- package/dist/story-generator/productionGitignoreManager.d.ts +0 -91
- package/dist/story-generator/productionGitignoreManager.d.ts.map +0 -1
- package/dist/story-generator/productionGitignoreManager.js +0 -340
- package/dist/story-generator/storyServiceFactory.d.ts +0 -22
- package/dist/story-generator/storyServiceFactory.d.ts.map +0 -1
- package/dist/story-generator/storyServiceFactory.js +0 -97
- package/dist/story-generator/storyServiceInterface.d.ts +0 -85
- package/dist/story-generator/storyServiceInterface.d.ts.map +0 -1
- package/dist/story-generator/storyServiceInterface.js +0 -5
- package/dist/story-generator/storySync.d.ts +0 -68
- package/dist/story-generator/storySync.d.ts.map +0 -1
- 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 = {
|
package/dist/mcp-server/index.js
CHANGED
|
@@ -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
|
-
//
|
|
86
|
-
|
|
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
|
-
//
|
|
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;
|
|
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
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
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
|
|
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
|
-
//
|
|
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(`
|
|
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
|
|
246
|
-
const
|
|
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
|
-
|
|
252
|
-
.filter(file => file.endsWith('.stories.tsx'))
|
|
253
|
-
.
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
288
|
-
|
|
289
|
-
responseText +=
|
|
290
|
-
|
|
291
|
-
|
|
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 += `-
|
|
314
|
-
responseText += `-
|
|
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,
|
|
402
|
+
// If no storyId provided, find the most recently modified story file
|
|
457
403
|
if (!storyId) {
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
if (
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
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;
|
|
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
|
-
|
|
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
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
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
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
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
|
-
|
|
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;
|
|
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"}
|