@tpitre/story-ui 1.7.0 ā 2.0.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/.env.sample +3 -1
- package/README.md +160 -606
- package/dist/cli/index.js +23 -24
- package/dist/cli/setup.js +256 -36
- package/dist/mcp-server/index.js +67 -0
- package/dist/mcp-server/routes/generateStory.js +323 -56
- package/dist/story-generator/componentBlacklist.js +181 -0
- package/dist/story-generator/componentDiscovery.js +9 -2
- package/dist/story-generator/configLoader.js +109 -39
- package/dist/story-generator/considerationsLoader.js +204 -0
- package/dist/story-generator/documentation-sources.js +36 -0
- package/dist/story-generator/documentationLoader.js +214 -0
- package/dist/story-generator/dynamicPackageDiscovery.js +527 -0
- package/dist/story-generator/enhancedComponentDiscovery.js +369 -118
- package/dist/story-generator/generateStory.js +7 -3
- package/dist/story-generator/postProcessStory.js +71 -0
- package/dist/story-generator/promptGenerator.js +286 -37
- package/dist/story-generator/storyHistory.js +118 -0
- package/dist/story-generator/storyTracker.js +33 -18
- package/dist/story-generator/storyValidator.js +39 -0
- package/dist/story-generator/universalDesignSystemAdapter.js +209 -0
- package/dist/story-generator/validateStory.js +82 -7
- package/dist/story-ui.config.js +12 -5
- package/package.json +11 -6
- package/templates/StoryUI/StoryUIPanel.stories.tsx +29 -13
- package/templates/StoryUI/StoryUIPanel.tsx +489 -359
- package/templates/react-import-rule.json +36 -0
- package/templates/story-generation-rules.json +29 -0
- package/templates/story-ui-considerations.json +156 -0
- package/templates/story-ui-considerations.md +109 -0
- package/templates/story-ui-docs-README.md +55 -0
package/dist/cli/index.js
CHANGED
|
@@ -7,6 +7,7 @@ import { fileURLToPath } from 'url';
|
|
|
7
7
|
import { setupProductionGitignore } from '../story-generator/productionGitignoreManager.js';
|
|
8
8
|
import { createStoryUIConfig } from '../story-ui.config.js';
|
|
9
9
|
import { setupCommand } from './setup.js';
|
|
10
|
+
import net from 'net';
|
|
10
11
|
const __filename = fileURLToPath(import.meta.url);
|
|
11
12
|
const __dirname = path.dirname(__filename);
|
|
12
13
|
const program = new Command();
|
|
@@ -25,16 +26,34 @@ program
|
|
|
25
26
|
.description('Start the Story UI server')
|
|
26
27
|
.option('-p, --port <port>', 'Port to run the server on', '4001')
|
|
27
28
|
.option('-c, --config <config>', 'Path to configuration file')
|
|
28
|
-
.action((options) => {
|
|
29
|
+
.action(async (options) => {
|
|
29
30
|
console.log('š Starting Story UI server...');
|
|
30
31
|
// Use absolute path to avoid dist/dist issue when package is linked
|
|
31
32
|
const pkgRoot = path.resolve(__dirname, '..');
|
|
32
33
|
const serverPath = path.join(pkgRoot, 'mcp-server/index.js');
|
|
33
34
|
console.log(`ā
Using MCP server at: ${serverPath}`);
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
// FIRST_EDIT: determine an available port
|
|
36
|
+
const requestedPort = parseInt(options.port || '4001', 10);
|
|
37
|
+
const isPortFree = (port) => {
|
|
38
|
+
return new Promise((resolve) => {
|
|
39
|
+
const tester = net.createServer()
|
|
40
|
+
.once('error', () => resolve(false))
|
|
41
|
+
.once('listening', () => {
|
|
42
|
+
tester.close();
|
|
43
|
+
resolve(true);
|
|
44
|
+
})
|
|
45
|
+
.listen(port);
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
let finalPort = requestedPort;
|
|
49
|
+
// eslint-disable-next-line no-await-in-loop
|
|
50
|
+
while (!(await isPortFree(finalPort))) {
|
|
51
|
+
finalPort += 1;
|
|
37
52
|
}
|
|
53
|
+
if (finalPort !== requestedPort) {
|
|
54
|
+
console.log(`ā ļø Port ${requestedPort} is in use. Using ${finalPort} instead.`);
|
|
55
|
+
}
|
|
56
|
+
const env = { ...process.env, PORT: String(finalPort) };
|
|
38
57
|
if (options.config) {
|
|
39
58
|
env.STORY_UI_CONFIG_PATH = options.config;
|
|
40
59
|
}
|
|
@@ -131,26 +150,6 @@ async function autoDetectAndCreateConfig() {
|
|
|
131
150
|
async function createTemplateConfig(template) {
|
|
132
151
|
let config;
|
|
133
152
|
switch (template) {
|
|
134
|
-
case 'material-ui':
|
|
135
|
-
config = {
|
|
136
|
-
importPath: '@mui/material',
|
|
137
|
-
componentPrefix: '',
|
|
138
|
-
layoutRules: {
|
|
139
|
-
multiColumnWrapper: 'Grid',
|
|
140
|
-
columnComponent: 'Grid',
|
|
141
|
-
layoutExamples: {
|
|
142
|
-
twoColumn: `<Grid container spacing={2}>
|
|
143
|
-
<Grid item xs={6}>
|
|
144
|
-
<Card>Left content</Card>
|
|
145
|
-
</Grid>
|
|
146
|
-
<Grid item xs={6}>
|
|
147
|
-
<Card>Right content</Card>
|
|
148
|
-
</Grid>
|
|
149
|
-
</Grid>`
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
};
|
|
153
|
-
break;
|
|
154
153
|
case 'chakra-ui':
|
|
155
154
|
config = {
|
|
156
155
|
importPath: '@chakra-ui/react',
|
package/dist/cli/setup.js
CHANGED
|
@@ -4,8 +4,91 @@ import inquirer from 'inquirer';
|
|
|
4
4
|
import chalk from 'chalk';
|
|
5
5
|
import { autoDetectDesignSystem } from '../story-generator/configLoader.js';
|
|
6
6
|
import { fileURLToPath } from 'url';
|
|
7
|
+
import net from 'net';
|
|
8
|
+
import { execSync } from 'child_process';
|
|
7
9
|
const __filename = fileURLToPath(import.meta.url);
|
|
8
10
|
const __dirname = path.dirname(__filename);
|
|
11
|
+
// FIRST_EDIT: helper functions to check for free ports
|
|
12
|
+
async function isPortAvailable(port) {
|
|
13
|
+
return new Promise((resolve) => {
|
|
14
|
+
const tester = net.createServer()
|
|
15
|
+
.once('error', () => resolve(false))
|
|
16
|
+
.once('listening', () => {
|
|
17
|
+
tester.close();
|
|
18
|
+
resolve(true);
|
|
19
|
+
})
|
|
20
|
+
.listen(port);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
async function findAvailablePort(startPort) {
|
|
24
|
+
let port = startPort;
|
|
25
|
+
// eslint-disable-next-line no-await-in-loop
|
|
26
|
+
while (!(await isPortAvailable(port))) {
|
|
27
|
+
port += 1;
|
|
28
|
+
}
|
|
29
|
+
return port;
|
|
30
|
+
}
|
|
31
|
+
// Design system installation configurations
|
|
32
|
+
const DESIGN_SYSTEM_CONFIGS = {
|
|
33
|
+
antd: {
|
|
34
|
+
packages: ['antd'],
|
|
35
|
+
name: 'Ant Design',
|
|
36
|
+
importPath: 'antd',
|
|
37
|
+
additionalSetup: 'import "antd/dist/reset.css";'
|
|
38
|
+
},
|
|
39
|
+
mantine: {
|
|
40
|
+
packages: ['@mantine/core', '@mantine/hooks', '@mantine/notifications'],
|
|
41
|
+
name: 'Mantine',
|
|
42
|
+
importPath: '@mantine/core',
|
|
43
|
+
additionalSetup: 'import "@mantine/core/styles.css";'
|
|
44
|
+
},
|
|
45
|
+
chakra: {
|
|
46
|
+
packages: ['@chakra-ui/react', '@emotion/react', '@emotion/styled', 'framer-motion'],
|
|
47
|
+
name: 'Chakra UI',
|
|
48
|
+
importPath: '@chakra-ui/react',
|
|
49
|
+
additionalSetup: 'import { ChakraProvider } from "@chakra-ui/react";'
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
async function installDesignSystem(systemKey) {
|
|
53
|
+
const config = DESIGN_SYSTEM_CONFIGS[systemKey];
|
|
54
|
+
const packageJson = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf-8'));
|
|
55
|
+
// Check if packages are already installed
|
|
56
|
+
const dependencies = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
57
|
+
const missingPackages = config.packages.filter(pkg => !dependencies[pkg]);
|
|
58
|
+
if (missingPackages.length === 0) {
|
|
59
|
+
console.log(chalk.green(`ā
${config.name} packages already installed`));
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
console.log(chalk.blue(`\nš¦ Installing ${config.name} packages...`));
|
|
63
|
+
console.log(chalk.gray(`Packages: ${missingPackages.join(', ')}`));
|
|
64
|
+
// Detect package manager
|
|
65
|
+
const npmLock = fs.existsSync(path.join(process.cwd(), 'package-lock.json'));
|
|
66
|
+
const yarnLock = fs.existsSync(path.join(process.cwd(), 'yarn.lock'));
|
|
67
|
+
const pnpmLock = fs.existsSync(path.join(process.cwd(), 'pnpm-lock.yaml'));
|
|
68
|
+
let installCommand = `npm install ${missingPackages.join(' ')}`;
|
|
69
|
+
if (yarnLock) {
|
|
70
|
+
installCommand = `yarn add ${missingPackages.join(' ')}`;
|
|
71
|
+
}
|
|
72
|
+
else if (pnpmLock) {
|
|
73
|
+
installCommand = `pnpm add ${missingPackages.join(' ')}`;
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
console.log(chalk.gray(`Running: ${installCommand}`));
|
|
77
|
+
execSync(installCommand, { stdio: 'inherit' });
|
|
78
|
+
console.log(chalk.green(`ā
${config.name} installed successfully!`));
|
|
79
|
+
if (config.additionalSetup) {
|
|
80
|
+
console.log(chalk.blue('\nš Additional setup required:'));
|
|
81
|
+
console.log(chalk.gray(`Add this import to your main CSS/index file:`));
|
|
82
|
+
console.log(chalk.cyan(`${config.additionalSetup}`));
|
|
83
|
+
}
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
console.error(chalk.red(`ā Failed to install ${config.name}:`), error);
|
|
88
|
+
console.log(chalk.yellow(`\nš” You can install manually with: ${installCommand}`));
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
9
92
|
export async function setupCommand() {
|
|
10
93
|
console.log(chalk.blue.bold('\nšØ Story UI Setup\n'));
|
|
11
94
|
console.log('This will help you configure Story UI for your design system.\n');
|
|
@@ -60,14 +143,25 @@ export async function setupCommand() {
|
|
|
60
143
|
message: 'Which design system are you using?',
|
|
61
144
|
choices: [
|
|
62
145
|
{ name: 'š¤ Auto-detect from package.json', value: 'auto' },
|
|
63
|
-
{ name: '
|
|
64
|
-
{ name: '
|
|
65
|
-
{ name: '
|
|
66
|
-
{ name: 'šÆ Mantine (@mantine/core)', value: 'mantine' },
|
|
146
|
+
{ name: 'š Ant Design (antd) - Install & Configure', value: 'antd' },
|
|
147
|
+
{ name: 'šÆ Mantine (@mantine/core) - Install & Configure', value: 'mantine' },
|
|
148
|
+
{ name: 'ā” Chakra UI (@chakra-ui/react) - Install & Configure', value: 'chakra' },
|
|
67
149
|
{ name: 'š§ Custom/Other', value: 'custom' }
|
|
68
150
|
],
|
|
69
151
|
default: autoDetected ? 'auto' : 'custom'
|
|
70
152
|
},
|
|
153
|
+
{
|
|
154
|
+
type: 'confirm',
|
|
155
|
+
name: 'installDesignSystem',
|
|
156
|
+
message: (answers) => {
|
|
157
|
+
const systemName = answers.designSystem === 'antd' ? 'Ant Design' :
|
|
158
|
+
answers.designSystem === 'mantine' ? 'Mantine' :
|
|
159
|
+
answers.designSystem === 'chakra' ? 'Chakra UI' : 'the design system';
|
|
160
|
+
return `Would you like to install ${systemName} and its dependencies now?`;
|
|
161
|
+
},
|
|
162
|
+
when: (answers) => ['antd', 'mantine', 'chakra'].includes(answers.designSystem),
|
|
163
|
+
default: true
|
|
164
|
+
},
|
|
71
165
|
{
|
|
72
166
|
type: 'input',
|
|
73
167
|
name: 'importPath',
|
|
@@ -96,6 +190,22 @@ export async function setupCommand() {
|
|
|
96
190
|
default: './src/components',
|
|
97
191
|
when: (answers) => answers.designSystem === 'custom'
|
|
98
192
|
},
|
|
193
|
+
{
|
|
194
|
+
type: 'input',
|
|
195
|
+
name: 'mcpPort',
|
|
196
|
+
message: 'Port for the Story UI MCP server',
|
|
197
|
+
default: async () => {
|
|
198
|
+
const port = await findAvailablePort(4001);
|
|
199
|
+
return String(port);
|
|
200
|
+
},
|
|
201
|
+
validate: async (input) => {
|
|
202
|
+
const value = parseInt(input, 10);
|
|
203
|
+
if (isNaN(value) || value <= 0)
|
|
204
|
+
return 'Enter a valid port number';
|
|
205
|
+
const available = await isPortAvailable(value);
|
|
206
|
+
return available ? true : `Port ${value} is already in use`;
|
|
207
|
+
}
|
|
208
|
+
},
|
|
99
209
|
{
|
|
100
210
|
type: 'confirm',
|
|
101
211
|
name: 'hasApiKey',
|
|
@@ -110,53 +220,53 @@ export async function setupCommand() {
|
|
|
110
220
|
validate: (input) => input.trim() ? true : 'API key is required'
|
|
111
221
|
}
|
|
112
222
|
]);
|
|
223
|
+
// Install design system if requested
|
|
224
|
+
if (answers.installDesignSystem && ['antd', 'mantine', 'chakra'].includes(answers.designSystem)) {
|
|
225
|
+
const installSuccess = await installDesignSystem(answers.designSystem);
|
|
226
|
+
if (!installSuccess) {
|
|
227
|
+
console.log(chalk.yellow('ā ļø Installation failed but continuing with configuration...'));
|
|
228
|
+
}
|
|
229
|
+
}
|
|
113
230
|
// Generate configuration
|
|
114
231
|
let config = {};
|
|
115
232
|
if (answers.designSystem === 'auto' && autoDetected) {
|
|
116
233
|
config = autoDetected;
|
|
117
234
|
}
|
|
118
|
-
else if (answers.designSystem === '
|
|
235
|
+
else if (answers.designSystem === 'chakra') {
|
|
119
236
|
config = {
|
|
120
|
-
importPath: '@
|
|
237
|
+
importPath: '@chakra-ui/react',
|
|
121
238
|
componentPrefix: '',
|
|
122
239
|
layoutRules: {
|
|
123
|
-
multiColumnWrapper: '
|
|
124
|
-
columnComponent: '
|
|
240
|
+
multiColumnWrapper: 'SimpleGrid',
|
|
241
|
+
columnComponent: 'Box',
|
|
125
242
|
containerComponent: 'Container',
|
|
126
243
|
layoutExamples: {
|
|
127
|
-
twoColumn: `<
|
|
128
|
-
<
|
|
244
|
+
twoColumn: `<SimpleGrid columns={2} spacing={6}>
|
|
245
|
+
<Box>
|
|
129
246
|
<Card>
|
|
130
|
-
<
|
|
131
|
-
<
|
|
132
|
-
|
|
133
|
-
|
|
247
|
+
<CardHeader>
|
|
248
|
+
<Heading size="md">Left Card</Heading>
|
|
249
|
+
</CardHeader>
|
|
250
|
+
<CardBody>
|
|
251
|
+
<Text>Left content goes here</Text>
|
|
252
|
+
</CardBody>
|
|
134
253
|
</Card>
|
|
135
|
-
</
|
|
136
|
-
<
|
|
254
|
+
</Box>
|
|
255
|
+
<Box>
|
|
137
256
|
<Card>
|
|
138
|
-
<
|
|
139
|
-
<
|
|
140
|
-
|
|
141
|
-
|
|
257
|
+
<CardHeader>
|
|
258
|
+
<Heading size="md">Right Card</Heading>
|
|
259
|
+
</CardHeader>
|
|
260
|
+
<CardBody>
|
|
261
|
+
<Text>Right content goes here</Text>
|
|
262
|
+
</CardBody>
|
|
142
263
|
</Card>
|
|
143
|
-
</
|
|
144
|
-
</
|
|
264
|
+
</Box>
|
|
265
|
+
</SimpleGrid>`
|
|
145
266
|
}
|
|
146
267
|
}
|
|
147
268
|
};
|
|
148
269
|
}
|
|
149
|
-
else if (answers.designSystem === 'chakra') {
|
|
150
|
-
config = {
|
|
151
|
-
importPath: '@chakra-ui/react',
|
|
152
|
-
componentPrefix: '',
|
|
153
|
-
layoutRules: {
|
|
154
|
-
multiColumnWrapper: 'SimpleGrid',
|
|
155
|
-
columnComponent: 'Box',
|
|
156
|
-
containerComponent: 'Container'
|
|
157
|
-
}
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
270
|
else if (answers.designSystem === 'antd') {
|
|
161
271
|
config = {
|
|
162
272
|
importPath: 'antd',
|
|
@@ -164,7 +274,21 @@ export async function setupCommand() {
|
|
|
164
274
|
layoutRules: {
|
|
165
275
|
multiColumnWrapper: 'Row',
|
|
166
276
|
columnComponent: 'Col',
|
|
167
|
-
containerComponent: 'div'
|
|
277
|
+
containerComponent: 'div',
|
|
278
|
+
layoutExamples: {
|
|
279
|
+
twoColumn: `<Row gutter={16}>
|
|
280
|
+
<Col span={12}>
|
|
281
|
+
<Card title="Left Card" bordered={false}>
|
|
282
|
+
<p>Left content goes here</p>
|
|
283
|
+
</Card>
|
|
284
|
+
</Col>
|
|
285
|
+
<Col span={12}>
|
|
286
|
+
<Card title="Right Card" bordered={false}>
|
|
287
|
+
<p>Right content goes here</p>
|
|
288
|
+
</Card>
|
|
289
|
+
</Col>
|
|
290
|
+
</Row>`
|
|
291
|
+
}
|
|
168
292
|
}
|
|
169
293
|
};
|
|
170
294
|
}
|
|
@@ -175,7 +299,27 @@ export async function setupCommand() {
|
|
|
175
299
|
layoutRules: {
|
|
176
300
|
multiColumnWrapper: 'SimpleGrid',
|
|
177
301
|
columnComponent: 'div',
|
|
178
|
-
containerComponent: 'Container'
|
|
302
|
+
containerComponent: 'Container',
|
|
303
|
+
layoutExamples: {
|
|
304
|
+
twoColumn: `<SimpleGrid cols={2} spacing="md">
|
|
305
|
+
<div>
|
|
306
|
+
<Card shadow="sm" padding="lg" radius="md" withBorder>
|
|
307
|
+
<Text fw={500} size="lg" mb="xs">Left Card</Text>
|
|
308
|
+
<Text size="sm" c="dimmed">
|
|
309
|
+
Left content goes here
|
|
310
|
+
</Text>
|
|
311
|
+
</Card>
|
|
312
|
+
</div>
|
|
313
|
+
<div>
|
|
314
|
+
<Card shadow="sm" padding="lg" radius="md" withBorder>
|
|
315
|
+
<Text fw={500} size="lg" mb="xs">Right Card</Text>
|
|
316
|
+
<Text size="sm" c="dimmed">
|
|
317
|
+
Right content goes here
|
|
318
|
+
</Text>
|
|
319
|
+
</Card>
|
|
320
|
+
</div>
|
|
321
|
+
</SimpleGrid>`
|
|
322
|
+
}
|
|
179
323
|
}
|
|
180
324
|
};
|
|
181
325
|
}
|
|
@@ -236,6 +380,38 @@ export async function setupCommand() {
|
|
|
236
380
|
console.warn(chalk.yellow(`ā ļø Template file not found: ${file}`));
|
|
237
381
|
}
|
|
238
382
|
}
|
|
383
|
+
// Create considerations file
|
|
384
|
+
const considerationsTemplatePath = path.resolve(__dirname, '../../templates/story-ui-considerations.md');
|
|
385
|
+
const considerationsPath = path.join(process.cwd(), 'story-ui-considerations.md');
|
|
386
|
+
if (!fs.existsSync(considerationsPath) && fs.existsSync(considerationsTemplatePath)) {
|
|
387
|
+
let considerationsContent = fs.readFileSync(considerationsTemplatePath, 'utf-8');
|
|
388
|
+
// Customize based on selected design system
|
|
389
|
+
if (config.importPath) {
|
|
390
|
+
considerationsContent = considerationsContent.replace('[Your Component Library]', config.importPath);
|
|
391
|
+
considerationsContent = considerationsContent.replace('[your-import-path]', config.importPath);
|
|
392
|
+
}
|
|
393
|
+
fs.writeFileSync(considerationsPath, considerationsContent);
|
|
394
|
+
console.log(chalk.green('ā
Created story-ui-considerations.md for AI customization'));
|
|
395
|
+
}
|
|
396
|
+
// Create documentation directory structure
|
|
397
|
+
const docsDir = path.join(process.cwd(), 'story-ui-docs');
|
|
398
|
+
if (!fs.existsSync(docsDir)) {
|
|
399
|
+
console.log(chalk.blue('\nš Creating documentation directory structure...'));
|
|
400
|
+
// Create main directory and subdirectories
|
|
401
|
+
const subdirs = ['guidelines', 'tokens', 'components', 'patterns'];
|
|
402
|
+
fs.mkdirSync(docsDir, { recursive: true });
|
|
403
|
+
for (const subdir of subdirs) {
|
|
404
|
+
fs.mkdirSync(path.join(docsDir, subdir), { recursive: true });
|
|
405
|
+
}
|
|
406
|
+
// Copy README template
|
|
407
|
+
const docsReadmeTemplatePath = path.resolve(__dirname, '../../templates/story-ui-docs-README.md');
|
|
408
|
+
const docsReadmePath = path.join(docsDir, 'README.md');
|
|
409
|
+
if (fs.existsSync(docsReadmeTemplatePath)) {
|
|
410
|
+
fs.writeFileSync(docsReadmePath, fs.readFileSync(docsReadmeTemplatePath, 'utf-8'));
|
|
411
|
+
}
|
|
412
|
+
console.log(chalk.green('ā
Created story-ui-docs/ directory structure'));
|
|
413
|
+
console.log(chalk.gray(' Add your design system documentation to enhance AI story generation'));
|
|
414
|
+
}
|
|
239
415
|
// Create .env file from template
|
|
240
416
|
const envSamplePath = path.resolve(__dirname, '../../.env.sample');
|
|
241
417
|
const envPath = path.join(process.cwd(), '.env');
|
|
@@ -246,6 +422,10 @@ export async function setupCommand() {
|
|
|
246
422
|
if (answers.apiKey) {
|
|
247
423
|
envContent = envContent.replace('your-claude-api-key-here', answers.apiKey);
|
|
248
424
|
}
|
|
425
|
+
// Update the VITE_STORY_UI_PORT with the chosen port
|
|
426
|
+
if (answers.mcpPort) {
|
|
427
|
+
envContent = envContent.replace('VITE_STORY_UI_PORT=4001', `VITE_STORY_UI_PORT=${answers.mcpPort}`);
|
|
428
|
+
}
|
|
249
429
|
fs.writeFileSync(envPath, envContent);
|
|
250
430
|
console.log(chalk.green(`ā
Created .env file${answers.apiKey ? ' with your API key' : ''}`));
|
|
251
431
|
}
|
|
@@ -277,15 +457,55 @@ export async function setupCommand() {
|
|
|
277
457
|
// Update package.json with convenience scripts
|
|
278
458
|
if (packageJson) {
|
|
279
459
|
const scripts = packageJson.scripts || {};
|
|
460
|
+
// FIRST_EDIT: include chosen port in script
|
|
461
|
+
const portFlag = `--port ${answers.mcpPort || '4001'}`;
|
|
280
462
|
if (!scripts['story-ui']) {
|
|
281
|
-
scripts['story-ui'] =
|
|
463
|
+
scripts['story-ui'] = `story-ui start ${portFlag}`;
|
|
464
|
+
}
|
|
465
|
+
else if (!scripts['story-ui'].includes('--port')) {
|
|
466
|
+
scripts['story-ui'] += ` ${portFlag}`;
|
|
282
467
|
}
|
|
283
468
|
if (!scripts['storybook-with-ui'] && scripts['storybook']) {
|
|
284
469
|
scripts['storybook-with-ui'] = 'concurrently "npm run storybook" "npm run story-ui"';
|
|
285
470
|
}
|
|
286
471
|
packageJson.scripts = scripts;
|
|
472
|
+
// Check and add required dependencies
|
|
473
|
+
const dependencies = packageJson.dependencies || {};
|
|
474
|
+
const devDependencies = packageJson.devDependencies || {};
|
|
475
|
+
let needsInstall = false;
|
|
476
|
+
// Check for concurrently (needed for storybook-with-ui script)
|
|
477
|
+
if (!dependencies['concurrently'] && !devDependencies['concurrently']) {
|
|
478
|
+
console.log(chalk.blue('š¦ Adding concurrently dependency...'));
|
|
479
|
+
devDependencies['concurrently'] = '^8.2.0';
|
|
480
|
+
needsInstall = true;
|
|
481
|
+
}
|
|
482
|
+
packageJson.dependencies = dependencies;
|
|
483
|
+
packageJson.devDependencies = devDependencies;
|
|
287
484
|
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
|
|
288
485
|
console.log(chalk.green('ā
Added convenience scripts to package.json'));
|
|
486
|
+
if (needsInstall) {
|
|
487
|
+
console.log(chalk.blue('\nš¦ Installing required dependencies...'));
|
|
488
|
+
console.log(chalk.gray('This may take a moment...\n'));
|
|
489
|
+
// Detect package manager
|
|
490
|
+
const npmLock = fs.existsSync(path.join(process.cwd(), 'package-lock.json'));
|
|
491
|
+
const yarnLock = fs.existsSync(path.join(process.cwd(), 'yarn.lock'));
|
|
492
|
+
const pnpmLock = fs.existsSync(path.join(process.cwd(), 'pnpm-lock.yaml'));
|
|
493
|
+
let installCommand = 'npm install';
|
|
494
|
+
if (yarnLock) {
|
|
495
|
+
installCommand = 'yarn install';
|
|
496
|
+
}
|
|
497
|
+
else if (pnpmLock) {
|
|
498
|
+
installCommand = 'pnpm install';
|
|
499
|
+
}
|
|
500
|
+
try {
|
|
501
|
+
execSync(installCommand, { stdio: 'inherit' });
|
|
502
|
+
console.log(chalk.green('ā
Dependencies installed successfully'));
|
|
503
|
+
}
|
|
504
|
+
catch (error) {
|
|
505
|
+
console.log(chalk.yellow('ā ļø Failed to install dependencies automatically.'));
|
|
506
|
+
console.log(chalk.yellow(` Please run "${installCommand}" manually to complete the setup.`));
|
|
507
|
+
}
|
|
508
|
+
}
|
|
289
509
|
}
|
|
290
510
|
console.log(chalk.green.bold('\nš Setup complete!\n'));
|
|
291
511
|
console.log(`š Configuration saved to: ${chalk.cyan(configPath)}`);
|
package/dist/mcp-server/index.js
CHANGED
|
@@ -17,6 +17,7 @@ import { getSyncedStories, deleteSyncedStory, clearAllSyncedStories, syncChatHis
|
|
|
17
17
|
import { setupProductionGitignore } from '../story-generator/productionGitignoreManager.js';
|
|
18
18
|
import { getInMemoryStoryService } from '../story-generator/inMemoryStoryService.js';
|
|
19
19
|
import { loadUserConfig } from '../story-generator/configLoader.js';
|
|
20
|
+
import fs from 'fs';
|
|
20
21
|
const app = express();
|
|
21
22
|
app.use(cors());
|
|
22
23
|
app.use(express.json());
|
|
@@ -40,6 +41,72 @@ app.delete('/mcp/sync/stories/:id', deleteSyncedStory);
|
|
|
40
41
|
app.delete('/mcp/sync/stories', clearAllSyncedStories);
|
|
41
42
|
app.get('/mcp/sync/chat-history', syncChatHistory);
|
|
42
43
|
app.get('/mcp/sync/validate/:id', validateChatSession);
|
|
44
|
+
// Proxy routes for frontend compatibility (maps /story-ui/ to /mcp/)
|
|
45
|
+
app.get('/story-ui/stories', getStoriesMetadata);
|
|
46
|
+
app.get('/story-ui/stories/:id', getStoryById);
|
|
47
|
+
app.get('/story-ui/stories/:id/content', getStoryContent);
|
|
48
|
+
app.delete('/story-ui/stories/:id', deleteStory);
|
|
49
|
+
app.delete('/story-ui/stories', clearAllStories);
|
|
50
|
+
app.post('/story-ui/generate', generateStoryFromPrompt);
|
|
51
|
+
app.post('/story-ui/claude', claudeProxy);
|
|
52
|
+
app.get('/story-ui/components', getComponents);
|
|
53
|
+
app.get('/story-ui/props', getProps);
|
|
54
|
+
app.get('/story-ui/memory-stats', getMemoryStats);
|
|
55
|
+
// Legacy delete route for backwards compatibility
|
|
56
|
+
app.post('/story-ui/delete', async (req, res) => {
|
|
57
|
+
try {
|
|
58
|
+
const { chatId, storyId } = req.body;
|
|
59
|
+
const id = chatId || storyId; // Support both parameter names
|
|
60
|
+
if (!id) {
|
|
61
|
+
return res.status(400).json({ error: 'chatId or storyId is required' });
|
|
62
|
+
}
|
|
63
|
+
console.log(`šļø Attempting to delete story: ${id}`);
|
|
64
|
+
// First try in-memory deletion (production mode)
|
|
65
|
+
const storyService = getInMemoryStoryService(config);
|
|
66
|
+
const inMemoryDeleted = storyService.deleteStory(id);
|
|
67
|
+
if (inMemoryDeleted) {
|
|
68
|
+
console.log(`ā
Deleted story from memory: ${id}`);
|
|
69
|
+
return res.json({
|
|
70
|
+
success: true,
|
|
71
|
+
message: 'Story deleted successfully from memory'
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
// If not found in memory, try file-system deletion (development mode)
|
|
75
|
+
if (gitignoreManager && !gitignoreManager.isProductionMode()) {
|
|
76
|
+
const storiesPath = config.generatedStoriesPath;
|
|
77
|
+
console.log(`š Searching for file-system story in: ${storiesPath}`);
|
|
78
|
+
if (fs.existsSync(storiesPath)) {
|
|
79
|
+
const files = fs.readdirSync(storiesPath);
|
|
80
|
+
const matchingFile = files.find(file => file.includes(id) || file.replace('.stories.tsx', '') === id);
|
|
81
|
+
if (matchingFile) {
|
|
82
|
+
const filePath = path.join(storiesPath, matchingFile);
|
|
83
|
+
fs.unlinkSync(filePath);
|
|
84
|
+
console.log(`ā
Deleted story file: ${filePath}`);
|
|
85
|
+
return res.json({
|
|
86
|
+
success: true,
|
|
87
|
+
message: 'Story deleted successfully from file system'
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
console.log(`ā Story not found: ${id}`);
|
|
93
|
+
return res.status(404).json({
|
|
94
|
+
success: false,
|
|
95
|
+
error: 'Story not found'
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
console.error('Error in legacy delete route:', error);
|
|
100
|
+
res.status(500).json({ error: 'Failed to delete story' });
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
// Synchronized story proxy routes
|
|
104
|
+
app.get('/story-ui/sync/stories', getSyncedStories);
|
|
105
|
+
app.get('/story-ui/sync/stories/:id', getSyncedStoryById);
|
|
106
|
+
app.delete('/story-ui/sync/stories/:id', deleteSyncedStory);
|
|
107
|
+
app.delete('/story-ui/sync/stories', clearAllSyncedStories);
|
|
108
|
+
app.get('/story-ui/sync/chat-history', syncChatHistory);
|
|
109
|
+
app.get('/story-ui/sync/validate/:id', validateChatSession);
|
|
43
110
|
// Set up production-ready gitignore and directory structure on startup
|
|
44
111
|
const config = loadUserConfig();
|
|
45
112
|
const gitignoreManager = setupProductionGitignore(config);
|