create-fs-cli 1.0.0 → 1.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-fs-cli",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "CLI tool to scaffold full-stack applications with interactive prompts",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -18,6 +18,7 @@ export async function createProject(projectName) {
18
18
  type: 'input',
19
19
  name: 'projectName',
20
20
  message: 'Project name:',
21
+ prefix: chalk.cyan('?'),
21
22
  default: 'my-fullstack-app',
22
23
  validate: (input) => {
23
24
  if (!/^[a-zA-Z0-9_-]+$/.test(input)) {
@@ -33,16 +34,18 @@ export async function createProject(projectName) {
33
34
  type: 'list',
34
35
  name: 'frontend',
35
36
  message: 'Choose frontend framework:',
37
+ prefix: chalk.cyan('?'),
36
38
  choices: [
37
39
  { name: 'Next.js', value: 'nextjs' },
38
40
  { name: 'React + Vite', value: 'react-vite' },
39
- { name: 'Svelte', value: 'svelte' }
41
+ { name: 'SvelteKit', value: 'svelte' }
40
42
  ]
41
43
  },
42
44
  {
43
45
  type: 'list',
44
46
  name: 'backend',
45
47
  message: 'Choose backend framework:',
48
+ prefix: chalk.cyan('?'),
46
49
  choices: [
47
50
  { name: 'Next.js API Routes (integrated)', value: 'nextjs-api' },
48
51
  { name: 'Express', value: 'express' },
@@ -54,6 +57,7 @@ export async function createProject(projectName) {
54
57
  type: 'list',
55
58
  name: 'database',
56
59
  message: 'Choose database:',
60
+ prefix: chalk.cyan('?'),
57
61
  choices: [
58
62
  { name: 'PostgreSQL', value: 'postgres' },
59
63
  { name: 'MongoDB', value: 'mongodb' },
@@ -66,6 +70,7 @@ export async function createProject(projectName) {
66
70
 
67
71
  // Step 1: Gather all information
68
72
  const answers = await inquirer.prompt(prompts);
73
+ console.log(); // Add spacing after prompts
69
74
 
70
75
  // Use CLI argument if provided, otherwise use prompted value
71
76
  if (projectName) {
@@ -74,7 +79,7 @@ export async function createProject(projectName) {
74
79
 
75
80
  // Validate backend/database combination
76
81
  if (answers.backend === 'fastapi' && answers.database === 'mysql') {
77
- console.log(chalk.yellow('\n⚠️ FastAPI template does not support MySQL. Defaulting to PostgreSQL.\n'));
82
+ console.log(`\n ${chalk.yellow('⚠')} ${chalk.bold('Warning')} ${chalk.dim('· FastAPI template does not support MySQL. Defaulting to PostgreSQL.')}\n`);
78
83
  answers.database = 'postgres';
79
84
  }
80
85
 
@@ -86,20 +91,21 @@ export async function createProject(projectName) {
86
91
  {
87
92
  type: 'confirm',
88
93
  name: 'overwrite',
94
+ prefix: chalk.yellow('⚠'),
89
95
  message: `Directory ${answers.projectName} already exists. Overwrite?`,
90
96
  default: false
91
97
  }
92
98
  ]);
93
99
 
94
100
  if (!overwrite) {
95
- console.log(chalk.red('Aborted.'));
101
+ console.log(chalk.red('\n Aborted.\n'));
96
102
  process.exit(1);
97
103
  }
98
104
  await fs.remove(projectPath);
99
105
  }
100
106
 
101
107
  await fs.ensureDir(projectPath);
102
- console.log(chalk.green(`\n📁 Created project directory: ${answers.projectName}\n`));
108
+ console.log(`\n ${chalk.green('✔')} ${chalk.bold('Project Directory')} ${chalk.dim('·')} ${answers.projectName}`);
103
109
 
104
110
  // Step 2: Generate frontend
105
111
  await generateFrontend(answers, projectPath);
@@ -119,10 +125,11 @@ export async function createProject(projectName) {
119
125
  await initializeGit(projectPath);
120
126
 
121
127
  // Step 7: Show success message
128
+ console.log(); // Empty line before success message
122
129
  showSuccessMessage(answers);
123
130
 
124
131
  } catch (error) {
125
- console.error(chalk.red('\n❌ Error:'), error.message);
132
+ console.error(`\n ${chalk.red('✖')} ${chalk.bold('Error')} ${chalk.dim('·')} ${error.message}\n`);
126
133
  process.exit(1);
127
134
  }
128
135
  }
@@ -215,7 +222,7 @@ The frontend includes a visual indicator in the top-right corner showing backend
215
222
  }
216
223
 
217
224
  async function cleanupProject(projectPath) {
218
- const spinner = ora('Cleaning up...').start();
225
+ const spinner = ora({ text: 'Cleaning up...', color: 'cyan' }).start();
219
226
 
220
227
  try {
221
228
  // Remove .git from frontend (created by create-next-app, create-vite, etc.)
@@ -230,22 +237,22 @@ async function cleanupProject(projectPath) {
230
237
  await fs.remove(backendGit);
231
238
  }
232
239
 
233
- spinner.succeed('Project cleaned up!');
240
+ spinner.succeed(chalk.dim('Project cleaned up'));
234
241
  } catch (error) {
235
- spinner.warn('Cleanup warning: ' + error.message);
242
+ spinner.warn(chalk.yellow('Cleanup warning: ' + error.message));
236
243
  }
237
244
  }
238
245
 
239
246
  async function initializeGit(projectPath) {
240
- const spinner = ora('Initializing git repository...').start();
247
+ const spinner = ora({ text: 'Initializing git repository...', color: 'cyan' }).start();
241
248
 
242
249
  try {
243
250
  await execa('git', ['init'], { cwd: projectPath });
244
251
  await execa('git', ['add', '.'], { cwd: projectPath });
245
252
  await execa('git', ['commit', '-m', 'Initial commit from FullStack CLI'], { cwd: projectPath });
246
253
 
247
- spinner.succeed('Git repository initialized!');
254
+ spinner.succeed(chalk.dim('Git repository initialized'));
248
255
  } catch (error) {
249
- spinner.warn('Git init skipped: ' + error.message);
256
+ spinner.warn(chalk.yellow('Git init skipped: ' + error.message));
250
257
  }
251
258
  }
@@ -23,15 +23,17 @@ const DATABASE_BRANCHES = {
23
23
  export async function generateBackend(answers, projectPath) {
24
24
  const { backend, database } = answers;
25
25
 
26
- const spinner = ora('Setting up backend...').start();
26
+ console.log(`\n${chalk.cyan('◯')} ${chalk.bold('Backend')} ${chalk.dim('· Setting up')} ${chalk.white(backend)}`);
27
+
28
+ const spinner = ora({ text: `Initializing...`, color: 'cyan' }).start();
27
29
 
28
30
  try {
29
31
  const templateUrl = TEMPLATES[backend];
30
32
  const branch = DATABASE_BRANCHES[database];
31
33
  const backendPath = path.join(projectPath, 'backend');
32
34
  const isPython = backend === 'fastapi';
33
-
34
- // Clone the template
35
+
36
+ // Update spinner text
35
37
  spinner.text = `Cloning ${backend} template (${branch} branch)...`;
36
38
 
37
39
  await execa('git', [
@@ -66,19 +68,16 @@ export async function generateBackend(answers, projectPath) {
66
68
 
67
69
  if (isPython) {
68
70
  // For Python, just show message (user needs venv)
69
- spinner.succeed(`Backend (${backend}) ready!`);
70
- console.log(chalk.yellow(' ⚠️ Run: cd backend && pip install -r requirements.txt\n'));
71
+ spinner.succeed(chalk.dim('Backend ready'));
72
+ console.log(` ${chalk.yellow('⚠')} ${chalk.dim('Run: cd backend && pip install -r requirements.txt')}\n`);
71
73
  } else {
72
74
  // For Node.js backends, auto-install
73
75
  await execa('npm', ['install'], { cwd: backendPath });
74
- spinner.succeed(`Backend (${backend}) ready! Dependencies installed.`);
76
+ spinner.succeed(chalk.dim('Backend ready (dependencies installed)'));
75
77
  }
76
-
77
- console.log(chalk.gray(` Template: ${templateUrl}`));
78
- console.log(chalk.gray(` Branch: ${branch}\n`));
79
78
 
80
79
  } catch (error) {
81
- spinner.fail('Backend setup failed');
80
+ spinner.fail(chalk.red('Backend setup failed'));
82
81
  throw new Error(`Failed to setup backend: ${error.message}`);
83
82
  }
84
83
  }
@@ -4,7 +4,7 @@ import chalk from 'chalk';
4
4
  import ora from 'ora';
5
5
 
6
6
  export async function injectBackendStatus(frontendPath, framework, isTypeScript, isIntegrated) {
7
- const spinner = ora('Adding BackendStatus component...').start();
7
+ const spinner = ora({ text: 'Adding BackendStatus component...', color: 'cyan' }).start();
8
8
 
9
9
  try {
10
10
  switch (framework) {
@@ -18,10 +18,9 @@ export async function injectBackendStatus(frontendPath, framework, isTypeScript,
18
18
  await injectSvelte(frontendPath, isTypeScript);
19
19
  break;
20
20
  }
21
- spinner.succeed('BackendStatus component added!');
21
+ spinner.succeed(chalk.dim('BackendStatus component added'));
22
22
  } catch (error) {
23
- spinner.warn(`Could not auto-inject BackendStatus: ${error.message}`);
24
- console.log(chalk.yellow(' You can manually add the BackendStatus component later.\n'));
23
+ spinner.warn(chalk.yellow(`Could not auto-inject BackendStatus: ${error.message}`));
25
24
  }
26
25
  }
27
26
 
@@ -71,30 +70,45 @@ import { useEffect, useState } from 'react'
71
70
  type Status = 'checking' | 'connected' | 'disconnected'
72
71
 
73
72
  export default function BackendStatus() {
74
- const [status, setStatus] = useState<Status>('checking')
73
+ const [backendStatus, setBackendStatus] = useState<Status>('checking')
74
+ const [dbStatus, setDbStatus] = useState<Status | null>(null)
75
75
 
76
76
  useEffect(() => {
77
- const checkBackend = async () => {
77
+ const checkHealth = async () => {
78
78
  try {
79
79
  const res = await fetch('${backendUrl}')
80
- setStatus(res.ok ? 'connected' : 'disconnected')
80
+ if (res.ok) {
81
+ const data = await res.json()
82
+ setBackendStatus('connected')
83
+ if (typeof data.database === 'boolean') {
84
+ setDbStatus(data.database ? 'connected' : 'disconnected')
85
+ }
86
+ } else {
87
+ setBackendStatus('disconnected')
88
+ setDbStatus(prev => prev !== null ? 'disconnected' : null)
89
+ }
81
90
  } catch {
82
- setStatus('disconnected')
91
+ setBackendStatus('disconnected')
92
+ setDbStatus(prev => prev !== null ? 'disconnected' : null)
83
93
  }
84
94
  }
85
95
 
86
- checkBackend()
87
- const interval = setInterval(checkBackend, 5000)
96
+ checkHealth()
97
+ const interval = setInterval(checkHealth, 5000)
88
98
  return () => clearInterval(interval)
89
99
  }, [])
90
100
 
91
- const statusConfig = {
92
- checking: { dot: '#fbbf24', text: 'Checking...' },
93
- connected: { dot: '#22c55e', text: 'Connected' },
94
- disconnected: { dot: '#ef4444', text: 'Disconnected' }
101
+ const colors: Record<Status, string> = {
102
+ checking: '#fbbf24',
103
+ connected: '#22c55e',
104
+ disconnected: '#ef4444'
95
105
  }
96
106
 
97
- const { dot, text } = statusConfig[status]
107
+ const labels: Record<Status, string> = {
108
+ checking: 'Checking...',
109
+ connected: 'Connected',
110
+ disconnected: 'Disconnected'
111
+ }
98
112
 
99
113
  return (
100
114
  <div style={{
@@ -106,10 +120,10 @@ export default function BackendStatus() {
106
120
  border: '1px solid rgba(255, 255, 255, 0.1)',
107
121
  color: 'rgba(255, 255, 255, 0.9)',
108
122
  padding: '0.5rem 0.875rem',
109
- borderRadius: '9999px',
123
+ borderRadius: '0.75rem',
110
124
  display: 'flex',
111
- alignItems: 'center',
112
- gap: '0.5rem',
125
+ flexDirection: 'column',
126
+ gap: '0.375rem',
113
127
  zIndex: 50,
114
128
  fontFamily: 'system-ui, -apple-system, sans-serif',
115
129
  fontSize: '0.8125rem',
@@ -117,14 +131,28 @@ export default function BackendStatus() {
117
131
  letterSpacing: '-0.01em',
118
132
  transition: 'all 0.2s ease'
119
133
  }}>
120
- <span style={{
121
- width: '8px',
122
- height: '8px',
123
- borderRadius: '50%',
124
- backgroundColor: dot,
125
- boxShadow: \`0 0 8px \${dot}\`
126
- }} />
127
- <span>Backend: {text}</span>
134
+ <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
135
+ <span style={{
136
+ width: '8px',
137
+ height: '8px',
138
+ borderRadius: '50%',
139
+ backgroundColor: colors[backendStatus],
140
+ boxShadow: \`0 0 8px \${colors[backendStatus]}\`
141
+ }} />
142
+ <span>Backend: {labels[backendStatus]}</span>
143
+ </div>
144
+ {dbStatus && (
145
+ <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
146
+ <span style={{
147
+ width: '8px',
148
+ height: '8px',
149
+ borderRadius: '50%',
150
+ backgroundColor: colors[dbStatus],
151
+ boxShadow: \`0 0 8px \${colors[dbStatus]}\`
152
+ }} />
153
+ <span>Database: {labels[dbStatus]}</span>
154
+ </div>
155
+ )}
128
156
  </div>
129
157
  )
130
158
  }
@@ -135,30 +163,45 @@ export default function BackendStatus() {
135
163
  import { useEffect, useState } from 'react'
136
164
 
137
165
  export default function BackendStatus() {
138
- const [status, setStatus] = useState('checking')
166
+ const [backendStatus, setBackendStatus] = useState('checking')
167
+ const [dbStatus, setDbStatus] = useState(null)
139
168
 
140
169
  useEffect(() => {
141
- const checkBackend = async () => {
170
+ const checkHealth = async () => {
142
171
  try {
143
172
  const res = await fetch('${backendUrl}')
144
- setStatus(res.ok ? 'connected' : 'disconnected')
173
+ if (res.ok) {
174
+ const data = await res.json()
175
+ setBackendStatus('connected')
176
+ if (typeof data.database === 'boolean') {
177
+ setDbStatus(data.database ? 'connected' : 'disconnected')
178
+ }
179
+ } else {
180
+ setBackendStatus('disconnected')
181
+ setDbStatus(prev => prev !== null ? 'disconnected' : null)
182
+ }
145
183
  } catch {
146
- setStatus('disconnected')
184
+ setBackendStatus('disconnected')
185
+ setDbStatus(prev => prev !== null ? 'disconnected' : null)
147
186
  }
148
187
  }
149
188
 
150
- checkBackend()
151
- const interval = setInterval(checkBackend, 5000)
189
+ checkHealth()
190
+ const interval = setInterval(checkHealth, 5000)
152
191
  return () => clearInterval(interval)
153
192
  }, [])
154
193
 
155
- const statusConfig = {
156
- checking: { dot: '#fbbf24', text: 'Checking...' },
157
- connected: { dot: '#22c55e', text: 'Connected' },
158
- disconnected: { dot: '#ef4444', text: 'Disconnected' }
194
+ const colors = {
195
+ checking: '#fbbf24',
196
+ connected: '#22c55e',
197
+ disconnected: '#ef4444'
159
198
  }
160
199
 
161
- const { dot, text } = statusConfig[status]
200
+ const labels = {
201
+ checking: 'Checking...',
202
+ connected: 'Connected',
203
+ disconnected: 'Disconnected'
204
+ }
162
205
 
163
206
  return (
164
207
  <div style={{
@@ -170,10 +213,10 @@ export default function BackendStatus() {
170
213
  border: '1px solid rgba(255, 255, 255, 0.1)',
171
214
  color: 'rgba(255, 255, 255, 0.9)',
172
215
  padding: '0.5rem 0.875rem',
173
- borderRadius: '9999px',
216
+ borderRadius: '0.75rem',
174
217
  display: 'flex',
175
- alignItems: 'center',
176
- gap: '0.5rem',
218
+ flexDirection: 'column',
219
+ gap: '0.375rem',
177
220
  zIndex: 50,
178
221
  fontFamily: 'system-ui, -apple-system, sans-serif',
179
222
  fontSize: '0.8125rem',
@@ -181,14 +224,28 @@ export default function BackendStatus() {
181
224
  letterSpacing: '-0.01em',
182
225
  transition: 'all 0.2s ease'
183
226
  }}>
184
- <span style={{
185
- width: '8px',
186
- height: '8px',
187
- borderRadius: '50%',
188
- backgroundColor: dot,
189
- boxShadow: \`0 0 8px \${dot}\`
190
- }} />
191
- <span>Backend: {text}</span>
227
+ <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
228
+ <span style={{
229
+ width: '8px',
230
+ height: '8px',
231
+ borderRadius: '50%',
232
+ backgroundColor: colors[backendStatus],
233
+ boxShadow: \`0 0 8px \${colors[backendStatus]}\`
234
+ }} />
235
+ <span>Backend: {labels[backendStatus]}</span>
236
+ </div>
237
+ {dbStatus && (
238
+ <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
239
+ <span style={{
240
+ width: '8px',
241
+ height: '8px',
242
+ borderRadius: '50%',
243
+ backgroundColor: colors[dbStatus],
244
+ boxShadow: \`0 0 8px \${colors[dbStatus]}\`
245
+ }} />
246
+ <span>Database: {labels[dbStatus]}</span>
247
+ </div>
248
+ )}
192
249
  </div>
193
250
  )
194
251
  }
@@ -258,30 +315,45 @@ function getReactBackendStatusCode(isTypeScript) {
258
315
  type Status = 'checking' | 'connected' | 'disconnected'
259
316
 
260
317
  export default function BackendStatus() {
261
- const [status, setStatus] = useState<Status>('checking')
318
+ const [backendStatus, setBackendStatus] = useState<Status>('checking')
319
+ const [dbStatus, setDbStatus] = useState<Status | null>(null)
262
320
 
263
321
  useEffect(() => {
264
- const checkBackend = async () => {
322
+ const checkHealth = async () => {
265
323
  try {
266
324
  const res = await fetch('http://localhost:5000/api/health')
267
- setStatus(res.ok ? 'connected' : 'disconnected')
325
+ if (res.ok) {
326
+ const data = await res.json()
327
+ setBackendStatus('connected')
328
+ if (typeof data.database === 'boolean') {
329
+ setDbStatus(data.database ? 'connected' : 'disconnected')
330
+ }
331
+ } else {
332
+ setBackendStatus('disconnected')
333
+ setDbStatus(prev => prev !== null ? 'disconnected' : null)
334
+ }
268
335
  } catch {
269
- setStatus('disconnected')
336
+ setBackendStatus('disconnected')
337
+ setDbStatus(prev => prev !== null ? 'disconnected' : null)
270
338
  }
271
339
  }
272
340
 
273
- checkBackend()
274
- const interval = setInterval(checkBackend, 5000)
341
+ checkHealth()
342
+ const interval = setInterval(checkHealth, 5000)
275
343
  return () => clearInterval(interval)
276
344
  }, [])
277
345
 
278
- const statusConfig = {
279
- checking: { dot: '#fbbf24', text: 'Checking...' },
280
- connected: { dot: '#22c55e', text: 'Connected' },
281
- disconnected: { dot: '#ef4444', text: 'Disconnected' }
346
+ const colors: Record<Status, string> = {
347
+ checking: '#fbbf24',
348
+ connected: '#22c55e',
349
+ disconnected: '#ef4444'
282
350
  }
283
351
 
284
- const { dot, text } = statusConfig[status]
352
+ const labels: Record<Status, string> = {
353
+ checking: 'Checking...',
354
+ connected: 'Connected',
355
+ disconnected: 'Disconnected'
356
+ }
285
357
 
286
358
  return (
287
359
  <div style={{
@@ -293,23 +365,37 @@ export default function BackendStatus() {
293
365
  border: '1px solid rgba(255, 255, 255, 0.1)',
294
366
  color: 'rgba(255, 255, 255, 0.9)',
295
367
  padding: '0.5rem 0.875rem',
296
- borderRadius: '9999px',
368
+ borderRadius: '0.75rem',
297
369
  display: 'flex',
298
- alignItems: 'center',
299
- gap: '0.5rem',
370
+ flexDirection: 'column',
371
+ gap: '0.375rem',
300
372
  zIndex: 50,
301
373
  fontFamily: 'system-ui, -apple-system, sans-serif',
302
374
  fontSize: '0.8125rem',
303
375
  fontWeight: 500
304
376
  }}>
305
- <span style={{
306
- width: '8px',
307
- height: '8px',
308
- borderRadius: '50%',
309
- backgroundColor: dot,
310
- boxShadow: \`0 0 8px \${dot}\`
311
- }} />
312
- <span>Backend: {text}</span>
377
+ <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
378
+ <span style={{
379
+ width: '8px',
380
+ height: '8px',
381
+ borderRadius: '50%',
382
+ backgroundColor: colors[backendStatus],
383
+ boxShadow: \`0 0 8px \${colors[backendStatus]}\`
384
+ }} />
385
+ <span>Backend: {labels[backendStatus]}</span>
386
+ </div>
387
+ {dbStatus && (
388
+ <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
389
+ <span style={{
390
+ width: '8px',
391
+ height: '8px',
392
+ borderRadius: '50%',
393
+ backgroundColor: colors[dbStatus],
394
+ boxShadow: \`0 0 8px \${colors[dbStatus]}\`
395
+ }} />
396
+ <span>Database: {labels[dbStatus]}</span>
397
+ </div>
398
+ )}
313
399
  </div>
314
400
  )
315
401
  }
@@ -319,30 +405,45 @@ export default function BackendStatus() {
319
405
  return `import { useEffect, useState } from 'react'
320
406
 
321
407
  export default function BackendStatus() {
322
- const [status, setStatus] = useState('checking')
408
+ const [backendStatus, setBackendStatus] = useState('checking')
409
+ const [dbStatus, setDbStatus] = useState(null)
323
410
 
324
411
  useEffect(() => {
325
- const checkBackend = async () => {
412
+ const checkHealth = async () => {
326
413
  try {
327
414
  const res = await fetch('http://localhost:5000/api/health')
328
- setStatus(res.ok ? 'connected' : 'disconnected')
415
+ if (res.ok) {
416
+ const data = await res.json()
417
+ setBackendStatus('connected')
418
+ if (typeof data.database === 'boolean') {
419
+ setDbStatus(data.database ? 'connected' : 'disconnected')
420
+ }
421
+ } else {
422
+ setBackendStatus('disconnected')
423
+ setDbStatus(prev => prev !== null ? 'disconnected' : null)
424
+ }
329
425
  } catch {
330
- setStatus('disconnected')
426
+ setBackendStatus('disconnected')
427
+ setDbStatus(prev => prev !== null ? 'disconnected' : null)
331
428
  }
332
429
  }
333
430
 
334
- checkBackend()
335
- const interval = setInterval(checkBackend, 5000)
431
+ checkHealth()
432
+ const interval = setInterval(checkHealth, 5000)
336
433
  return () => clearInterval(interval)
337
434
  }, [])
338
435
 
339
- const statusConfig = {
340
- checking: { dot: '#fbbf24', text: 'Checking...' },
341
- connected: { dot: '#22c55e', text: 'Connected' },
342
- disconnected: { dot: '#ef4444', text: 'Disconnected' }
436
+ const colors = {
437
+ checking: '#fbbf24',
438
+ connected: '#22c55e',
439
+ disconnected: '#ef4444'
343
440
  }
344
441
 
345
- const { dot, text } = statusConfig[status]
442
+ const labels = {
443
+ checking: 'Checking...',
444
+ connected: 'Connected',
445
+ disconnected: 'Disconnected'
446
+ }
346
447
 
347
448
  return (
348
449
  <div style={{
@@ -354,23 +455,37 @@ export default function BackendStatus() {
354
455
  border: '1px solid rgba(255, 255, 255, 0.1)',
355
456
  color: 'rgba(255, 255, 255, 0.9)',
356
457
  padding: '0.5rem 0.875rem',
357
- borderRadius: '9999px',
458
+ borderRadius: '0.75rem',
358
459
  display: 'flex',
359
- alignItems: 'center',
360
- gap: '0.5rem',
460
+ flexDirection: 'column',
461
+ gap: '0.375rem',
361
462
  zIndex: 50,
362
463
  fontFamily: 'system-ui, -apple-system, sans-serif',
363
464
  fontSize: '0.8125rem',
364
465
  fontWeight: 500
365
466
  }}>
366
- <span style={{
367
- width: '8px',
368
- height: '8px',
369
- borderRadius: '50%',
370
- backgroundColor: dot,
371
- boxShadow: \`0 0 8px \${dot}\`
372
- }} />
373
- <span>Backend: {text}</span>
467
+ <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
468
+ <span style={{
469
+ width: '8px',
470
+ height: '8px',
471
+ borderRadius: '50%',
472
+ backgroundColor: colors[backendStatus],
473
+ boxShadow: \`0 0 8px \${colors[backendStatus]}\`
474
+ }} />
475
+ <span>Backend: {labels[backendStatus]}</span>
476
+ </div>
477
+ {dbStatus && (
478
+ <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
479
+ <span style={{
480
+ width: '8px',
481
+ height: '8px',
482
+ borderRadius: '50%',
483
+ backgroundColor: colors[dbStatus],
484
+ boxShadow: \`0 0 8px \${colors[dbStatus]}\`
485
+ }} />
486
+ <span>Database: {labels[dbStatus]}</span>
487
+ </div>
488
+ )}
374
489
  </div>
375
490
  )
376
491
  }
@@ -441,43 +556,66 @@ function getSvelteBackendStatusCode() {
441
556
  return `<script>
442
557
  import { onMount, onDestroy } from 'svelte';
443
558
 
444
- let status = 'checking';
559
+ let backendStatus = 'checking';
560
+ let dbStatus = null;
445
561
  let interval;
446
562
 
447
- const statusConfig = {
448
- checking: { text: 'Checking', color: '#eab308' },
449
- connected: { text: 'Connected', color: '#22c55e' },
450
- disconnected: { text: 'Disconnected', color: '#ef4444' }
563
+ const colors = {
564
+ checking: '#fbbf24',
565
+ connected: '#22c55e',
566
+ disconnected: '#ef4444'
451
567
  };
452
568
 
453
- async function checkBackend() {
569
+ const labels = {
570
+ checking: 'Checking...',
571
+ connected: 'Connected',
572
+ disconnected: 'Disconnected'
573
+ };
574
+
575
+ async function checkHealth() {
454
576
  try {
455
577
  const res = await fetch('http://localhost:5000/api/health');
456
- status = res.ok ? 'connected' : 'disconnected';
578
+ if (res.ok) {
579
+ const data = await res.json();
580
+ backendStatus = 'connected';
581
+ if (typeof data.database === 'boolean') {
582
+ dbStatus = data.database ? 'connected' : 'disconnected';
583
+ }
584
+ } else {
585
+ backendStatus = 'disconnected';
586
+ if (dbStatus !== null) dbStatus = 'disconnected';
587
+ }
457
588
  } catch {
458
- status = 'disconnected';
589
+ backendStatus = 'disconnected';
590
+ if (dbStatus !== null) dbStatus = 'disconnected';
459
591
  }
460
592
  }
461
593
 
462
594
  onMount(() => {
463
- checkBackend();
464
- interval = setInterval(checkBackend, 5000);
595
+ checkHealth();
596
+ interval = setInterval(checkHealth, 5000);
465
597
  });
466
598
 
467
599
  onDestroy(() => {
468
600
  if (interval) clearInterval(interval);
469
601
  });
470
-
471
- $: config = statusConfig[status];
472
602
  </script>
473
603
 
474
- <div class="backend-status">
475
- <span class="dot" style="background-color: {config.color}; box-shadow: 0 0 8px {config.color};"></span>
476
- <span class="text">Backend: {config.text}</span>
604
+ <div class="status-container">
605
+ <div class="status-row">
606
+ <span class="dot" style="background-color: {colors[backendStatus]}; box-shadow: 0 0 8px {colors[backendStatus]};"></span>
607
+ <span class="text">Backend: {labels[backendStatus]}</span>
608
+ </div>
609
+ {#if dbStatus}
610
+ <div class="status-row">
611
+ <span class="dot" style="background-color: {colors[dbStatus]}; box-shadow: 0 0 8px {colors[dbStatus]};"></span>
612
+ <span class="text">Database: {labels[dbStatus]}</span>
613
+ </div>
614
+ {/if}
477
615
  </div>
478
616
 
479
617
  <style>
480
- .backend-status {
618
+ .status-container {
481
619
  position: fixed;
482
620
  top: 1rem;
483
621
  right: 1rem;
@@ -486,17 +624,23 @@ function getSvelteBackendStatusCode() {
486
624
  -webkit-backdrop-filter: blur(8px);
487
625
  color: rgba(255, 255, 255, 0.9);
488
626
  padding: 0.5rem 0.875rem;
489
- border-radius: 9999px;
627
+ border-radius: 0.75rem;
490
628
  border: 1px solid rgba(255, 255, 255, 0.1);
491
629
  box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.3);
492
630
  display: flex;
493
- align-items: center;
494
- gap: 0.5rem;
631
+ flex-direction: column;
632
+ gap: 0.375rem;
495
633
  z-index: 50;
496
634
  font-family: system-ui, -apple-system, sans-serif;
497
635
  font-size: 0.75rem;
498
636
  }
499
637
 
638
+ .status-row {
639
+ display: flex;
640
+ align-items: center;
641
+ gap: 0.5rem;
642
+ }
643
+
500
644
  .dot {
501
645
  width: 8px;
502
646
  height: 8px;
@@ -8,7 +8,7 @@ import { injectBackendStatus } from './backendStatus.js';
8
8
  export async function generateFrontend(answers, projectPath) {
9
9
  const { frontend } = answers;
10
10
 
11
- console.log(chalk.cyan('\n🎨 Setting up frontend...\n'));
11
+ console.log(`\n${chalk.cyan('◯')} ${chalk.bold('Frontend')} ${chalk.dim('· Setting up')} ${chalk.white(frontend)}`);
12
12
 
13
13
  switch (frontend) {
14
14
  case 'nextjs':
@@ -24,7 +24,7 @@ export async function generateFrontend(answers, projectPath) {
24
24
  }
25
25
 
26
26
  async function generateNextJS(answers, projectPath) {
27
- console.log(chalk.yellow('📦 Running create-next-app (follow the prompts)...\n'));
27
+ console.log(chalk.gray(' Running create-next-app (follow the prompts)...\n'));
28
28
 
29
29
  try {
30
30
  // Run Next.js CLI interactively
@@ -33,7 +33,7 @@ async function generateNextJS(answers, projectPath) {
33
33
  stdio: 'inherit'
34
34
  });
35
35
 
36
- console.log(chalk.green('\n✅ Next.js project created!\n'));
36
+ console.log(`\n ${chalk.green('✔')} ${chalk.dim('Next.js project created')}\n`);
37
37
 
38
38
  // Detect if TypeScript was chosen
39
39
  const frontendPath = path.join(projectPath, 'frontend');
@@ -49,7 +49,7 @@ async function generateNextJS(answers, projectPath) {
49
49
  }
50
50
 
51
51
  async function generateReactVite(answers, projectPath) {
52
- console.log(chalk.yellow('📦 Running create-vite (follow the prompts)...\n'));
52
+ console.log(chalk.gray(' Running create-vite (follow the prompts)...\n'));
53
53
 
54
54
  try {
55
55
  // Run Vite CLI interactively
@@ -58,7 +58,7 @@ async function generateReactVite(answers, projectPath) {
58
58
  stdio: 'inherit'
59
59
  });
60
60
 
61
- console.log(chalk.green('\n✅ React + Vite project created!\n'));
61
+ console.log(`\n ${chalk.green('✔')} ${chalk.dim('React + Vite project created')}\n`);
62
62
 
63
63
  // Detect if TypeScript was chosen
64
64
  const frontendPath = path.join(projectPath, 'frontend');
@@ -74,7 +74,7 @@ async function generateReactVite(answers, projectPath) {
74
74
  }
75
75
 
76
76
  async function generateSvelte(answers, projectPath) {
77
- console.log(chalk.yellow('📦 Running create-svelte (follow the prompts)...\n'));
77
+ console.log(chalk.gray(' Running create-svelte (follow the prompts)...\n'));
78
78
 
79
79
  try {
80
80
  // Run SvelteKit CLI interactively
@@ -83,7 +83,7 @@ async function generateSvelte(answers, projectPath) {
83
83
  stdio: 'inherit'
84
84
  });
85
85
 
86
- console.log(chalk.green('\n✅ SvelteKit project created!\n'));
86
+ console.log(`\n ${chalk.green('✔')} ${chalk.dim('SvelteKit project created')}\n`);
87
87
 
88
88
  // Detect if TypeScript was chosen
89
89
  const frontendPath = path.join(projectPath, 'frontend');
package/src/index.js CHANGED
@@ -10,10 +10,10 @@ const program = new Command();
10
10
  // Display banner
11
11
  console.log(
12
12
  chalk.cyan(
13
- figlet.textSync('FSCLI', { font: 'ANSI Shadow' })
13
+ figlet.textSync('create-fs-cli', { font: 'Standard' })
14
14
  )
15
15
  );
16
- console.log(chalk.gray(' Full-Stack CLI - Scaffold applications with ease\n'));
16
+ console.log(chalk.dim('\n Rapidly scaffold full-stack applications\n'));
17
17
 
18
18
  program
19
19
  .name('create-fs-cli')
@@ -17,61 +17,58 @@ export function showSuccessMessage(answers) {
17
17
  const hasBackend = backend !== 'nextjs-api';
18
18
  const isPython = backend === 'fastapi';
19
19
 
20
- let message = chalk.green.bold('✅ Project created successfully!') + '\n\n';
20
+ const log = [];
21
+
22
+ // Title
23
+ log.push(`${chalk.bgGreen.black.bold(' SUCCESS ')} ${chalk.green(`Created ${chalk.bold(projectName)}`)}\n`);
24
+
25
+ // Tech Stack (Tree style)
26
+ log.push(chalk.cyan.bold('Tech Stack'));
27
+ log.push(`${chalk.gray('├─')} Frontend ${chalk.white(frontendName)}`);
28
+ log.push(`${chalk.gray('├─')} Backend ${chalk.white(backendName)}`);
29
+ log.push(`${chalk.gray('└─')} Database ${chalk.white(dbName)}\n`);
30
+
31
+ // Next Steps
32
+ log.push(chalk.cyan.bold('Next Steps'));
21
33
 
22
- message += chalk.white('📦 Stack:\n');
23
- message += chalk.gray(` Frontend: ${frontendName}\n`);
24
- message += chalk.gray(` Backend: ${backendName}\n`);
25
- message += chalk.gray(` Database: ${dbName}\n\n`);
34
+ let stepNum = 1;
35
+ log.push(`${chalk.gray(`${stepNum++}.`)} cd ${projectName}`);
26
36
 
27
37
  if (hasBackend && database !== 'none') {
28
- message += chalk.yellow('⚙️ Configure database:\n');
29
- message += chalk.white(` Edit ${chalk.cyan('backend/.env')} with your credentials\n\n`);
38
+ log.push(`${chalk.gray(`${stepNum++}.`)} Configure database inside ${chalk.white.bold('backend/.env')}`);
30
39
  }
31
40
 
32
- message += chalk.yellow('📦 Install dependencies:\n');
33
- message += chalk.white(` cd ${projectName}\n`);
34
- message += chalk.white(` cd frontend && npm install\n`);
41
+ log.push(`${chalk.gray(`${stepNum++}.`)} Start development servers:\n`);
35
42
 
36
43
  if (hasBackend) {
37
- if (isPython) {
38
- message += chalk.white(` cd ../backend && pip install -r requirements.txt\n\n`);
39
- } else {
40
- message += chalk.white(` cd ../backend && npm install\n\n`);
41
- }
44
+ log.push(chalk.gray(' # Terminal 1 (Backend)'));
45
+ log.push(` ${chalk.cyan('cd')} backend`);
46
+ log.push(` ${chalk.cyan(isPython ? 'uvicorn main:app --reload --port 5000' : 'npm run dev')}\n`);
47
+
48
+ log.push(chalk.gray(' # Terminal 2 (Frontend)'));
49
+ log.push(` ${chalk.cyan('cd')} frontend`);
50
+ log.push(` ${chalk.cyan('npm run dev')}\n`);
42
51
  } else {
43
- message += '\n';
52
+ log.push(` ${chalk.cyan('cd')} frontend`);
53
+ log.push(` ${chalk.cyan('npm run dev')}\n`);
44
54
  }
45
55
 
46
- message += chalk.yellow('🚀 Start development:\n');
56
+ // Footer info
57
+ log.push(chalk.gray('─'.repeat(45)));
58
+ log.push(`${chalk.gray('Local:')} ${chalk.white('http://localhost:3000')}`);
47
59
 
48
60
  if (hasBackend) {
49
- message += chalk.gray(' # Terminal 1 - Backend:\n');
50
- message += chalk.white(` cd backend && ${isPython ? 'uvicorn main:app --reload --port 5000' : 'npm run dev'}\n\n`);
51
- message += chalk.gray(' # Terminal 2 - Frontend:\n');
61
+ log.push(`${chalk.gray('API:')} ${chalk.white('http://localhost:5000')}`);
52
62
  }
53
63
 
54
- message += chalk.white(` cd frontend && npm run dev\n\n`);
55
-
56
- message += chalk.cyan('🌐 URLs:\n');
57
- message += chalk.white(' Frontend: http://localhost:3000\n');
58
-
59
- if (hasBackend) {
60
- message += chalk.white(' Backend: http://localhost:5000\n');
61
- message += chalk.white(' Health: http://localhost:5000/api/health\n\n');
62
- } else {
63
- message += '\n';
64
- }
65
-
66
- message += chalk.magenta('👁️ Backend Status Indicator:\n');
67
- message += chalk.gray(' Look for the status badge in the top-right corner!');
64
+ log.push(`${chalk.gray('Note:')} Check the UI status badge for connection status.`);
68
65
 
69
66
  console.log(
70
- boxen(message, {
71
- padding: 1,
72
- margin: 1,
67
+ boxen(log.join('\n'), {
68
+ padding: { top: 1, bottom: 1, left: 2, right: 3 },
69
+ margin: { top: 1, bottom: 1, left: 0, right: 0 },
73
70
  borderStyle: 'round',
74
- borderColor: 'cyan'
71
+ borderColor: 'gray'
75
72
  })
76
73
  );
77
74
  }