@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.
- package/README.md +25 -72
- package/dist/cli/deploy.d.ts +0 -7
- package/dist/cli/deploy.d.ts.map +1 -1
- package/dist/cli/deploy.js +10 -421
- package/dist/cli/index.js +0 -29
- package/dist/mcp-server/index.js +20 -66
- package/dist/mcp-server/mcp-stdio-server.js +40 -110
- 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 +7 -7
- 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 -214
- 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 -147
- 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/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/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 { 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
|
-
//
|
|
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,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
|
-
//
|
|
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(`
|
|
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
|
|
247
|
-
const
|
|
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
|
-
|
|
253
|
-
.filter(file => file.endsWith('.stories.tsx'))
|
|
254
|
-
.
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
289
|
-
|
|
290
|
-
responseText +=
|
|
291
|
-
|
|
292
|
-
|
|
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 += `-
|
|
315
|
-
responseText += `-
|
|
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,
|
|
402
|
+
// If no storyId provided, find the most recently modified story file
|
|
458
403
|
if (!storyId) {
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
if (
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
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;
|
|
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
|
-
|
|
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"}
|